3d学习笔记(一)——游戏对象与资源

简答题

解释 游戏对象(GameObjects) 和 资源(Assets)的区别与联系

游戏对象(GameObject)

指的是一个能够容纳实现实际功能的组件。游戏中的每个对象都是一个游戏对象 (GameObject)。然而,游戏对象 (GameObjects) 本身不做任何事情。它们需要特殊属性 (special properties) 才能成为一个角色、一种环境或者一种特殊效果。但是每个对象要做很多不同的事情。如果每个对象都是一个游戏对象 (GameObject)

资源(Assets)

指的是在游戏中可能用到的各种东西,比如模型、声音文件、贴图文件等等

两者之间的联系

资源可以作为模板,实例化成具体的游戏对象,也可以作为游戏对象中的某种属性,同时也可以被多个游戏对象同时使用。


游戏案例分析

下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
资源的结构

  • 上面的样例可见,该游戏资源的内容分为预制、脚本等等,按照文件的类型的不同,添加到不同的文件夹当中,便于后面的资源整理和利用
游戏对象的结构

  • 由上面的样例可见,该游戏将游戏对象分为游戏的控制,环境的设置,玩家以及目标,符合游戏的各个必要的环节

触发条件分析

编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
常用事件包括 OnGUI() OnDisable() OnEnable()
执行代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class intbesh : MonoBehaviour {

    // Use this for initialization

    private void Awake()
    {
        Debug.Log("It is an awake!");
    }

    void Start () {
        Debug.Log("It is a start!");
	}
	
	// Update is called once per frame
    void Update () {
        Debug.Log("It is an update!");
	}

    private void FixedUpdate()
    {
        Debug.Log("It is a fixedupdate!");
    }

    private void OnGUI()
    {
        Debug.Log("It is ONGUI!");
    }

    private void OnDisable()
    {
        Debug.Log("It is OnDisable!");
    }

    private void OnEnable()
    {
        Debug.Log("It is OnEnable!");
    }
}
控制台的输出

分析

  • awake:当一个脚本实例被载入时被调用
  • start:在所有update函数之前被调用一次
  • update:当行为启用时,其update在每一帧被调用
  • fixedupdate:当行为启用时,其fixedupdate在每一时间片被调用
  • OnGUI:渲染和处理GUI事件时调用
  • OnEnable:当对象变为可用或激活状态时被调用
  • OnDisable:当对象变为不可用或非激活状态时被调用

分析三个对象

查找脚本手册,了解 GameObject,Transform,Component 对象
分别翻译官方对三个对象的描述(Description)
描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件
本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。
用 UML 图描述 三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图)
翻译
  • GameObject:是Unity场景里面所有实体的基类.
  • Transform:物体的位置、旋转和缩放。
  • Component:一切附加到游戏物体的基类。

描述属性
  • 第一个选择框是activeSelf:可以定义对象的名称,动静态等属性
  • 第二个选择框是Transform:可以定义对象的位置、面向方向、大小
  • 第三个选择框是Box Collider:可以调整坐标系的位置、大小
  • 第四个选择框是Component:可以给对象增加行为
UML图


对对象进行相关操作

整理相关学习资料,编写简单代码验证以下技术的实现:
查找对象
添加子对象
遍历对象树
清除所有子对象

一开始存在的对象树如下:



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

public class InitBeh : MonoBehaviour {

    public Transform Table;

    // Use this for initialization

    private void Awake()
    {
        Debug.Log("It is an awake!");
    }

    void Start () {

        Debug.Log("It is a start!");
        var cube = GameObject.Find("chair1");

        if(cube != null)
        {
            Debug.Log(cube);
        }
        else
        {
            Debug.Log("It is nothing!");
        }
	}
	
	// Update is called once per frame
	void Update () {
       
	}
}



  • 利用GameObject中的find函数即可对对象进行查找,首先声明一个变量,如果存在查找的对象,则返回该对象的名称,如果不存在,则返回null

添加子对象
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InitBeh : MonoBehaviour {

    public Transform Table;

    // Use this for initialization

    private void Awake()
    {
        Debug.Log("It is an awake!");
    }

    void Start () {

        Debug.Log("It is a start!");
        var cube = GameObject.Find("chair5");

        if(cube != null)
        {
            Debug.Log(cube);
        }
        else
        {
            Debug.Log("It is nothing!");
        }

        GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Cube);
        temp.name = "chair5";
        temp.transform.position = new Vector3(0, Random.Range(0, 5), 0);
        temp.transform.parent = this.transform;

        cube = GameObject.Find("chair5");

        if (cube != null)
        {
            Debug.Log(cube);
        }
        else
        {
            Debug.Log("It is nothing!");
        }
    }
	
	// Update is called once per frame
	void Update () {
       
	}
}


  • 通过GameObject中的CreatePrimitive函数,即可创建出一个制定类型的对象。同时通过创建的对象中的transform中的parent属性可以定义该对象所属的上级对象,同时通过name等属性,可以进一步对该新对象的属性进行修改。

遍历对象树
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DeskBeh : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
        foreach(Transform child in transform)
        {
            Debug.Log(child.name);
        }
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}

  • 通过一个循环,即可对当前行为添加的对象的子对象进行遍历。

清除所有子对象
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DeskBeh : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
        foreach(Transform child in transform)
        {
            Destroy(child.gameObject);
        }
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}


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

预设(Prefabs)有什么好处?
预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?
制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
预设(Prefabs)的好处

预设可以提前将设计游戏中所需要的游戏对象进行设计打包,成为一个模板。在设计的过程中,随时可以直接从资源当中加载,成为一个游戏对象。预设的存在,方便了面向对象思想的应用,方便我们在设计游戏中更加便捷。


预设和克隆之间的关系

克隆是将已经存在的游戏对象,或者是资源当中的预设进行复制。克隆可以是对已经添加在视图当中的游戏对象进行复制,也可以是对资源当中,我们提前预设好的模板进行克隆。因此,预设一般来说是克隆的一个基础步骤,在预设之后,就可以在资源当中随心所欲地为自己想要的对象进行克隆了。

代码实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class InitBeh : MonoBehaviour {

	// Use this for initialization
	void Start () {

        Debug.Log("start!");
        Object temp = Resources.Load("desk");
        GameObject cube = Instantiate(temp) as GameObject;

        cube.transform.position = new Vector3(0, Random.Range(5, 7), 0);
        cube.transform.parent = this.transform;
	}
	
	// Update is called once per frame
	void Update () {
		
	}
}

  • 通过提前制作出预制的desk,并且添加到Resources文件夹中,在空对象的行为当中,通过Resources中的load函数加载该预制,并将其进行实例化,再设置各种属性即可实现预制的实例化。


组合模式

尝试解释组合模式(Composite Pattern / 一种设计模式)。使用 BroadcastMessage() 方法
向子对象发送消息
组合模式的解释

组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。组合模式是将对象组合成树形结构以表示“部分-整体”的层次结构,它使得用户对单个对象和组合对象的使用具有一致性。

代码实现

父对象

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

public class DeskBeh : MonoBehaviour {

	// Use this for initialization
	void Start () {

        Debug.Log("Start!");
        this.BroadcastMessage("test");
	}

	// Update is called once per frame
	void Update () {
		
	}
}

子对象

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

public class ChairBeh : MonoBehaviour {

	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
		
	}

    void test()
    {
        Debug.Log("It is just a test");
    }
}

结果


  • 通过父对象调用BroadcastMessage函数,向所有的子对象广播test信息。所有的子对象接收该信息,并自动调用自身定义的test函数。

编程实践:小游戏

  • 游戏内容: 井字棋 或 贷款计算器 或 简单计算器 等等
  • 技术限制: 仅允许使用 IMGUI 构建 UI
  • 作业目的:
    • 提升 debug 能力
    • 提升阅读 API 文档能力
井字棋游戏

代码实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class game : MonoBehaviour
{

    private int empty = 9;
    private int turn = 1;
    private int[,] chess = new int[3, 3];

    // Use this for initialization
    void Start()
    {
        reset();
    }

    // Update is called once per frame
    void reset()
    {

        empty = 9;
        turn = 1;
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                chess[i, j] = 0;
            }
        }
    }

    private void OnGUI()
    {
        GUI.skin.button.fontSize = 20;
        GUI.skin.label.fontSize = 30;
        if(GUI.Button(new Rect(450,400,200,80), "Reset"))
        {
            reset();
        }

        int result = is_win();

        if(result == 1)
        {
            GUI.Label(new Rect(500, 20, 100, 50), "O wins");
        }
        else if (result == 2)
        {
            GUI.Label(new Rect(500, 20, 100, 50), "X wins");
        }
        else if (result == 3)
        {
            GUI.Label(new Rect(470, 20, 200, 50), "no one wins");
        }

        for(int i = 0; i < 3; i++)
        {
            for(int j = 0; j < 3; j++)
            {
                if (chess[i, j] == 1) GUI.Button(new Rect(i * 100 + 400, j * 100 + 80, 100, 100), "O");
                if (chess[i, j] == 2) GUI.Button(new Rect(i * 100 + 400, j * 100 + 80, 100, 100), "X");
                if(GUI.Button(new Rect(i * 100 + 400, j * 100 + 80, 100, 100), ""))
                {
                    if(result == 0)
                    {
                        if (turn == 1) chess[i, j] = 1;
                        if (turn == 2) chess[i, j] = 2;
                        empty--;
                        if(empty%2 == 1)
                        {
                            turn = 1;
                        }
                        else
                        {
                            turn = 2;
                        }
                    }
                }
            }
        }
    }

    int is_win()
    {
        int temp = chess[1, 1];
        if(temp != 0)
        {
            if (temp == chess[0, 0] && temp == chess[2, 2])
            {
                return temp;
            }
            if (temp == chess[0, 2] && temp == chess[2, 0])
            {
                return temp;
            }
            if (temp == chess[0, 1] && temp == chess[2, 1])
            {
                return temp;
            }
            if (temp == chess[1, 0] && temp == chess[1, 2])
            {
                return temp;
            }
        }
        //判断是否中心的十字形或者X字形
        temp = chess[0, 0];
        if(temp != 0)
        {
            if(temp == chess[0,1] && temp == chess[0,2])
            {
                return temp;
            }
            if (temp == chess[1, 0] && temp == chess[2, 0])
            {
                return temp;
            }
        }
        //判断是否是第一行或者第一列相同
        temp = chess[2, 2];
        if(temp != 0)
        {
            if(temp == chess[2,0] && temp == chess[2,1])
            {
                return temp;
            }
            if(temp == chess[0,2] && temp == chess[1,2])
            {
                return temp;
            }
        }
        //判断是否是第三行或者第三列相同
        if(empty == 0)
        {
            return 3;//没有空的地方,因此双方平局
        }
        else
        {
            return 0;//还未分出胜负
        }
    }
}

  • 首先是完善reset函数,以及判断输赢的is_win函数,再通过OnGUI进行UI的渲染
  • 之后将上述代码文件直接添加为摄像头的行为文件即可
界面效果

猜你喜欢

转载自blog.csdn.net/qq_36312878/article/details/79704354