3D游戏设计 homework2

1、简单题

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

区别:
游戏对象是出现在场景中的各种物体或场景。 即游戏中的每个对象都是一个游戏对象。游戏对象自己不做任何事。他们需要专有属性,才可以成为一个角色,一个环境,或一种特殊效果。游戏对象更像是一种容器。
资源是项目中的素材,包括图像、音乐文件、脚本文件、预制文件等。

联系:
游戏对象可保存为资源(以便重复利用),资源可以作为组件或游戏对象创建游戏对象实例。

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

资源的目录组织结构: 资源文件夹将作用相似的资源放在同一个文件夹中。

游戏对象树的层次结构: 游戏对象树类似于树,一个游戏对象往往是包括了多个子对象。子对象又包括了它的子对象

1.3 编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件

基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
常用事件包括 OnGUI() OnDisable() OnEnable()

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

public class init : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("It is in Start");
    }

    // Update is called once per frame
    void Update()
    {
        Debug.Log("It is in Update");
    }

    private void Awake()
    {
        Debug.Log("It is in Awake");
    }

    private void FixedUpdate()
    {
        Debug.Log("It is in FixedUpdate");
    }

    private void LateUpdate()
    {
        Debug.Log("It is in LateUpdate");
    }

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

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

    private void OnEnable()
    {
        Debug.Log("It is in OnEnable");
    }
}

执行效果:
执行效果
解释:
awake:当一个脚本实例被载入时被调用
start:在所有update函数之前被调用一次,用于初始化
update:当行为启用时,其update在每一帧被调用
fixedupdate:当行为启用时,其fixedupdate在每一时间片被调用
LateUpdate:在每帧执行完毕调用,在所有update结束后才执行,比较适合用于命令脚本的执行。
OnGUI:渲染和处理GUI事件时调用,一般在这个函数里绘制GUI菜单
OnEnable:当对象变为可用或激活状态时被调用
OnDisable:当对象变为不可用或非激活状态时被调用

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

分别翻译官方对三个对象的描述(Description)
描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件
本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。
用 UML 图描述 三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图)
table
翻译:
GameObject:游戏对象是Unity中表示游戏角色,游戏道具和游戏场景的基本对象。他们自身无法完成许多功能,但是他们构成了那些给予他们实体功能的组件的容器。
Transform:转换组件决定了游戏场景中每个游戏对象的位置,旋转和缩放比例。每个游戏对象都有转换组件。
Component:组件是游戏中对象和行为的细节。它是每个游戏对象的功能部分。

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

table 的 Transform 的属性:
Position为(0,0,0);Rotation为(0,0,0)、Scale为(1,1,1)。

table 的部件:
Tranform、Mesh Filter、Box Collider、Mesh Renderer

UML图:
UMl

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

查找对象

public static GameObject Find(string name); 
//通过名字查找
public static GameObject FindWithTag(string tag);
//通过标签查找单个对象
public static GameObject[] FindGameObjectsWithTag(string tag);
//通过标签查找多个对象

添加子对象

public static GameObect CreatePrimitive(PrimitiveTypetype)

遍历对象树

foreach (Transform child in transform) {
 
         Debug.Log(child.gameObject.name);
 
}

清除所有子对象

foreach (Transform child in transform) {  
    Destroy(child.gameObject);  
} 

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

预设(Prefabs)有什么好处?
预设像一个类模板,可以迅速方便创建大量相同属性的对象、操作简单,代码量少,减少出错概率。修改的复杂度降低,一旦需要修改所有相同属性的对象,只需要修改预设即可,所有通过预设实例化的对象都会做出相应变化。
预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?
修改预设后,所有通过预设实例化的对象都会做出相应变化。但是克隆只是复制它,这个对象独立于原来的对象,在修改克隆体不会影响原有的。这类似于C++值复制和引用的差别。
制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象

public GameObject table;
 void Start () {
        Debug.Log("ITStart");
        GameObject newTable = (GameObject)Instantiate(table.gameObject);
}

2、编程实践,小游戏

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

在阅读IMGUI的API后实现,具体代码如下:

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

public class newGame : MonoBehaviour
{   
    //引入图片
    public Texture letterX;
    public Texture circle;
    public Texture2D backGround;

    private int[,] board = new int[3, 3];           //储存棋盘信息,0为X,1为O,
    private int turn = 1;               //标记轮到谁,1为O,0为X
    private int count = 0;              //统计棋盘棋子数量
    private bool finished = false;      //标记游戏是否结束
    // Start is called before the first frame update
    void Start()
    {
        Reset();
    }

    void OnGUI()
    {
        GUI.Label(new Rect(100, 50, 210, 50), "Tic - Tac - Toe");   //游戏标题
        if (GUI.Button(new Rect(400, 50, 100, 50), "RESET")) {   //重置游戏按钮
            Reset();
        }

        //游戏过程
        for (int i = 0; i < 3; ++ i) {
            for (int j = 0; j < 3; ++ j) {
                //模拟轮次点击过程
                if (board[i, j] == 1) {
                    GUI.Button(new Rect(400 + 50 * i, 150 + 50 * j, 50, 50), circle);
                }
                else if (board[i, j] == 0) {
                    GUI.Button(new Rect(400 + 50 * i, 150 + 50 * j, 50, 50), letterX);
                }
                else {
                    if (GUI.Button(new Rect(400 + 50 * i, 150 + 50 * j, 50, 50), backGround)) {
                        if (finished == true)       //游戏结束便保持现状
                            continue;
                        count++;
                        if (turn == 1) {
                            board[i, j] = 1;
                            turn = 0;
                        }
                        else if (turn == 0) {
                            board[i, j] = 0;
                            turn = 1;
                        }
                    }
                }
            }
        }

        //得到结果并输出
        if (count >= 5)
        {
            int State = getResult();        //-2表示游戏未结束,-1表示平局,0表示X胜利,1表示O胜利
            if (State == 0) {
                GUI.Label(new Rect(400, 100, 100, 50), "X WIN!");
                finished = true;
            }
            else if (State == 1) {
                GUI.Label(new Rect(400, 100, 100, 50), "O WIN!");
                finished = true;
            }
            else if (State == -1 && count == 9) {
                GUI.Label(new Rect(400, 100, 210, 50), "NO ONE WIN");
            }
        }
    }

    int getResult()
    {
        //检验行是否存在满足胜利的条件
        for (int i = 0; i < 3; ++ i) {
            if (board[i, 0] == board[i, 1] && board[i, 0] == board[i, 2] && board[i, 0] != -1) {
                return board[i, 0]; 
            }
        }
        //检验列是否存在满足胜利的条件
        for (int j = 0; j < 3; ++ j) {
            if (board[0, j] == board[1, j] && board[0, j] == board[2, j] && board[0, j] != -1) {
                return board[0, j]; 
            }
        }

        //检验对角线是否存在满足胜利的条件
        if (board[0, 0] == board[1, 1] && board[0, 0] == board[2, 2] && board[0, 0] != -1)
            return board[0, 0];

        if (board[0, 2] == board[1, 1] && board[0, 2] == board[2, 0] && board[0, 2] != -1)
            return board[0, 2];

        //判断是否平局
        int counts = 0;
        for (int i = 0; i < 3; ++ i) {
            for (int j = 0; j < 3; ++ j) {
                if (board[i, j] != -1) {
                    counts++;
                }
                else
                    return -2;
            }
        }
        if (counts == 9) {
            return -1;
        }
        return -2;
    }

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

    }
    //重置游戏
    void Reset()
    {
        turn = 1;
        count = 0;
        finished = false;
        for (int i = 0; i < 3; ++ i) {
            for (int j = 0; j < 3; ++ j) {
                board[i, j] = -1;
            }
        }
    }
}

视频链接

3 思考题【选做】

3.1

微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们,我们称这种设计为“模板方法模式”。为什么是“模板方法”模式而不是“策略模式”呢?
模板方法模式定义了一个算法的步骤或者骨架,并允许子类别为一个或多个步骤提供其实践方式。让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤。而策略模式作为一种软件设计模式,指对象有某个行为,但是在不同的场景中,该行为有不同的实现算法。从二者的定义来分析,微软 XNA 引擎的 Game 对象的游戏循环的步骤一直都是一样的,故更符合模板方法模式。

3.2

将游戏对象组成树型结构,每个节点都是游戏对象(或数)。尝试解释组合模式(Composite Pattern / 一种设计模式)。
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。组合模式是将对象组合成树形结构以表示“部分-整体”的层次结构,它使得用户对单个对象和组合对象的使用具有一致性。
使用 BroadcastMessage() 方法,向子对象发送消息。你能写出 BroadcastMessage() 的伪代码吗?
父对象代码:

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

public class father : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("It is in fatherStart");
        this.BroadcastMessage("testBroad", "hello sons!");
    }

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

子对象代码:

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

public class son : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("It is in sonStart");
    }

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

    public void testBroad(string str)
    {
        print("son receive message: " + str);
    }
}

结果:结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值