Bootstrap

游戏开发之Unity-学习(一) —— Unity基础概念和实现井字棋

游戏开发之Unity-学习(一) —— Unity基础概念和实现井字棋

一、游戏对象与资源的区别和联系:

游戏对象是指游戏模型、音频、视频、UI文本、灯光等游戏中的对象,资源包括游戏对象,脚本文件、游戏字体、场景等。

二、资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)

1. 资源的目录组织结构一般是:

Asserts
  | - Animations
    | - MainMenu
    | - UI
  | - Audio
    | - Music
    | - SFX
    | - UI
  | - Data
    | - Agents
    | - Alignments
    | - Levels
  | - Documentation
  | - Fonts
  | - Gizmos
  | - Icon
  | - Materials
    | - Levels
    | - Particles
    | - Player
    | - Projectiles
    | - UI
    | - Units
  | - Models
    | - Levels
    | - Player
    | - Projectiles
    | - Units
  | - Particles
    | - Prefabs
    | - Textures
  | - PostProcessing
  | - Prefabs
    | - Audio
    | - Enemies
    | - Health
    | - Managers
    | - Player
    | - UI
  | - Scenes
    | - Levels
    | - MainMenu
  | - Scripts
    | - Core
  | - Sprites
  | - Textures
  | - UI

2. 对象组织的结构

对象一般有玩家、敌人、环境、摄像机和音乐等虚拟父类,这些父节点本身没有实体,但它们的子类真正包含了游戏中会出现的对象。

三、用 debug 语句来验证 MonoBehaviour基本行为或事件触发的条件



Unity 相关函数执行顺序如下图所示:

四、查找脚本手册,了解 GameObject,Transform,Component 对象

  • 分别翻译官方对三个对象的描述(Description)
    • GameObject :
      GameObjects are the fundamental objects in Unity that represent characters, props and scenery.
      GameObjects是Unity中代表人物,道具和风景的基本对象
    • Transform :
      The Transform component determines the Position, Rotation, and Scale of each object in the scene
      Transform 组件决定场景中每个对象的位置,旋转,和缩放。
    • Component :
      Base class for everything attached to GameObjects.
      所有附加到GameObject的基类。
  • table实体具有tag,Layer,Static,Transform等属性,这些属性均通过GameObject类的实例调用

    一个标签是可以分配给一个或多个参考字GameObjects。例如,您可以为玩家控制角色定义“玩家”标签,为非玩家控制角色定义“敌人”标签。您可以使用“可收藏”标签定义玩家可以在场景中收集的物品。

    标签可以帮助您识别GameObjects以进行脚本撰写。它们确保您无需使用拖放手动将GameObjects添加到脚本的暴露属性,从而在多个GameObjects中使用相同的脚本代码时节省时间。

    标签对碰撞控制脚本中的触发器非常有用; 他们需要确定玩家是否与敌人,道具或收藏品互动。

    您可以使用GameObject.FindWithTag()函数将GameObject设置为查找包含所需标记的任何对象来查找GameObject。

  • Transform有位置,缩放,旋转等属性,这些属性在MonoBehaviour类里使用。Table的组件包括脚本,五个cube,Transform,渲染器等

五、整理相关学习资料,编写简单代码验证以下技术的实现:

  • 查找对象:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {
    private void Start()
    {
        GameObject go = GameObject.Find("/Cube");
        if(null != go)
            Debug.Log("Find Cube");
    }
}
  • 添加子对象:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {
    private void Start()
    {
        GameObject go = GameObject.Find("/Cube");
        GameObject newCube = GameObject.Find("/Cube/Cube");
        GameObject newCubeInstance = Instantiate(newCube);
        newCubeInstance.transform.parent = go.transform;
    }
}
  • 遍历对象树:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {
    private void Start()
    {
        foreach (Transform child in transform)
        {
            child.position += Vector3.up * 1.5F;
        }
    }
}
  • 清除所有子对象:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    private void Start()
    {
        foreach (Transform child in transform)
        {
            Destroy(child.gameObject);
        }
    }
}

六、资源预设(Prefabs)与 对象克隆 (clone)

  • 预设(Prefabs)有什么好处
    预设(prefab)是一个对象的快照或模板,可以用于快速生成相同的对象,比如子弹、敌人等等。修改预设以后,通过该预设生成的对象也会发生变化
  • 预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系
    预设与克隆都能创建出相同的对象。预设创建出的对象与源预设依然有联系,后者的更改会影响到前者。但是克隆出的对象与源对象不再有联系。
  • 制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    public Transform prefab;
    private void Start()
    {
        Instantiate(prefab, new Vector3(0, 0, 0), Quaternion.identity);
    }
}
  • 尝试解释组合模式(Composite Pattern / 一种设计模式)。使用 BroadcastMessage() 方法
    组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript1 : MonoBehaviour {
    void ApplyDamage(float damage)
    {
        print(damage);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
    private void Start()
    {
        gameObject.BroadcastMessage("ApplyDamage", 5.0F);
    }
    void ApplyDamage(float damage)
    {
        print(damage);
    }
}

七、实现井字棋

基于Unity 2017.3.1f1 (64-bit)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {
    private int count;
    public Texture2D iconTrue;
    public Texture2D iconFalse;
    private int[] flag = { 0,0,0,0,0,0,0,0,0};
    private bool isEnd,iseq;

    private void Start()
    {
        count = 0;
        isEnd = false;
        iseq = false;
    }

    void OnGUI()
    {
        GUI.Label(new Rect(Screen.width / 2 - 25, Screen.height / 2 - 60 - 50, 50, 25), "井字棋");
        if (GUI.Button(new Rect(Screen.width / 2 - 25, Screen.height / 2 - 60-25, 50,25),"重置"))
        {
            for (int i = 0; i < 9; ++i)
                flag[i] = 0;
            count = 0;
            isEnd = false;
            iseq = false;
        }
        for(int i=0;i<3;++i)
        {
            for(int j=0;j<3;++j)
            {
                if(flag[i*3+j] == 0)
                {
                    if (GUI.Button(new Rect(Screen.width/2-60+(i * 40), Screen.height/2 - 60+(j * 40), 40, 40), "")&& !isEnd)
                    {
                        flag[i * 3 + j] = (count % 2) + 1;
                        count++;
                    }
                }
                else if(flag[i * 3 + j] == 1)
                {
                    GUI.Button(new Rect(Screen.width/2 - 60+ (i * 40), Screen.height/2 - 60 + (j * 40), 40, 40), iconTrue);
                }
                else if(flag[i * 3 + j] == 2)
                {
                    GUI.Button(new Rect(Screen.width/2 - 60+ (i * 40), Screen.height/2 - 60 + (j * 40), 40, 40), iconFalse);
                }
            }
        }
        for (int i = 0; i < 3; ++i)
        {
            if (flag[i*3]!=0&&flag[i * 3] == flag[i * 3 + 1] && flag[i * 3] == flag[i * 3 + 2])
            {
                isEnd = true;
                GUI.Label(new Rect(Screen.width / 2 - 20, Screen.height / 2 + 60, 40, 40), new GUIContent(flag[i * 3] == 1 ? iconTrue : iconFalse));
            }
        }
        for (int j = 0; j < 3; ++j)
        {
            if (flag[j]!=0&&flag[j] == flag[3 + j] && flag[j] == flag[6 + j])
            {
                isEnd = true;
                GUI.Label(new Rect(Screen.width / 2 - 20, Screen.height / 2 + 60, 40, 40), new GUIContent(flag[j] == 1 ? iconTrue : iconFalse));
            }
        }
        if(flag[4]!=0&&((flag[0]==flag[8]&&flag[0] == flag[4])||(flag[2] == flag[6] && flag[2] == flag[4])))
        {
            isEnd = true;
            GUI.Label(new Rect(Screen.width / 2 - 20, Screen.height / 2 + 60, 40, 40), new GUIContent(flag[4] == 1 ? iconTrue : iconFalse));
        }
        if (isEnd&&!iseq)
            GUI.Label(new Rect(Screen.width / 2 - 20, Screen.height / 2 + 100, 40, 40), "Win!");
        for(int i=0;i<9; ++i)
        {
            if (flag[i] == 0)
            {
                iseq = false;
                break;
            }
            else
                iseq = true;
        }
        if(iseq&&!isEnd)
        {
            GUI.Label(new Rect(Screen.width / 2 - 20, Screen.height / 2 + 100, 40, 40),"Draw");
        }
    }
}
;