Unity3D游戏编程——离散仿真作业

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/SYSU_yzt/article/details/100626441

Unity3D游戏编程——离散仿真作业

1.简答题asd

1.1解释游戏对象(GameObjects)和资源(Assets)的区别与联系
答:游戏对象(GameObjects)是一系列资源的组成序列。资源(Assets)是一系列可以被Unity采用的数据文件(如纹理,材质,音频等),只有有了相应的资源,在Unity项目中才能呈现出相应的效果(比如需要木质表面,项目文件中就必须有木质材质的资源文件),有了资源之后,我们便可以在Unity中利用已有的资源来构建我们各种各样的游戏对象了,游戏对象是游戏过程中的基本操作单位。
1.2下载几个游戏案例,分别总结资源,对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
答:我在Github选取了游戏《天天萌泡泡》作为游戏案例来进行分析。它的游戏对象树和资源目录结构如下:

在这里插入图片描述

从游戏对象树我们可以看到,该游戏的游戏对象树的底层先分了几个大的GameObject来承担不同方面的工作,然后各个大GameObject又包含小GameObject来分管某方面的细节性工作。

在这里插入图片描述
从资源目录结构我们可以看出,游戏将资源分为动画,音频,字体,图片,插件等方面来分别存储。

1.3编写一个代码,使用debug语句来验证MonoBehaviour基本行为或事件触发的条件,基本行为包括Awake,Start,Update,FixedUpadate,LateUpdate,常用事件包括OnGUI,OnDisable,OnEnable。
答:

代码如下:

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

public class FirstBeh : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("This Start!");
    }
    void Awake()
    {
        Debug.Log("This Awake!");
    }
    void FixedUpdate()
    {
        Debug.Log("This FixedUpdate!");
    }
    void LateUpdate()
    {
        Debug.Log("This LateUpdate!");
    }
    void OnGUI()
    {
        Debug.Log("This OnGUI!");
    }
    void OnDisable()
    {
        Debug.Log("This OnDisable!");
    }
    // Update is called once per frame
    void Update()
    {
        Debug.Log("This Update!");
    }
    void OnEnable()
    {
        Debug.Log("This Enabled!");
    }
}

代码运行效果如下:

开始:
在这里插入图片描述
运行中:
在这里插入图片描述
结束
在这里插入图片描述

1.4查找脚本手册,了解GameObject,Transform,Component对象。
翻译官方对三个对象的描述:1.GmaeObject是所有Unity场景中的对象的基类。 2.Transform:是一个对象的位置,大小,角度等属性。每个对象都有Transform属性,Transform也有层次组织,以达到相对位置的效果。 3.Component:每个对象都有的基本类,它包括脚本等组件。
描述Table对象(实体)的属性,Table的Transform的属性,Table的组件:

在这里插入图片描述
我们可以看到Inspector中第一栏就是activeSelf属性,它的Transform属性在第二栏,包括了位置(0,0,0)
,旋转(0,0,0),大小(1,1,1),除了activeSelf都是Component,包括transform,mesh render等,还包括我自己加入的脚本。
UML图如下:
在这里插入图片描述

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

查找对象:

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

public class SecnodBeh : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        var ob = GameObject.Find("table");
        if (ob != null)
        {
            Debug.Log("Success");
        }
    }

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

在这里插入图片描述
添加子对象:

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

public class SecnodBeh : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        GameObject ob = GameObject.CreatePrimitive(PrimitiveType.Cube);
        ob.name = "chair5";
        ob.transform.position = new Vector3(0, 5, 5);
        ob.transform.parent = this.transform;
    }

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

在这里插入图片描述

遍历对象树:

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

public class SecnodBeh : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("begin");
        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 SecnodBeh : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        for(int i = 0; i < transform.childCount; i++)
        {
            Destroy(transform.GetChild(i).gameObject);
        }
    }

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

在这里插入图片描述

1.6资源预设与对象克隆:

预设的好处:预设是指对GameObject的一种描述,它可以很方便的生成多个相同的对象,但他们实质上是同一份,有点类似于指针的感觉,预设能够方便我们重复性的创建某些功能形状都一致的对象。

预设与对象克隆的关系:他们的区别类似于C/C++中的深拷贝和浅拷贝,预设的修改会改变所有相关的对象,但是对象克隆产生的各个对象是独立的。

写一段代码将table预设资源实例化为游戏对象:

(GameObject)Instantiate(Resources.Load("table"));

使用这段代码的前提是要先将table预设进资源库中,此处参考了其它博客。

2.编程实践,小游戏

游戏内容:

井字棋

技术限制:

仅允许使用 IMGUI 构建 UI

作业目的:

了解 OnGUI() 事件,提升 debug 能力
提升阅读 API 文档能力

源代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CoreCode : MonoBehaviour
{
    private bool flag = false;
    private int[, ] state = new int[3, 3];
    private int turn = 1;
    private int AI = 0;
    GUIStyle white = new GUIStyle();
    GUIStyle black = new GUIStyle();
    // Start is called before the first frame update
    void Start()
    {
        for(int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                state[i, j] = 0;
            }
        }

        white.normal.background = null;
        white.normal.textColor = new Color(1, 1, 1);
        white.fontSize = 100;

        black.normal.background = null;
        black.normal.textColor = new Color(0, 0, 0);
        black.fontSize = 100;
    }
    void Restart()
    {
        for (int i = 0; i < 3; i++)
        {
            for (int j = 0; j < 3; j++)
            {
                state[i, j] = 0;
            }
        }
        turn = 1;
    }
    int check()
    {
        for (int i = 0; i < 3; i++)
        {
            if (state[i, 0] == state[i, 1] && state[i, 1] == state[i, 2] && state[i, 2] != 0)
            {
                return state[i, 0];
            }
        }

        for (int i = 0; i < 3; i++)
        {
            if (state[0, i] == state[1, i] && state[1, i] == state[2, i] && state[2, i] != 0)
            {
                return state[0, i];
            }
        }

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

        if (state[2, 0] == state[1, 1] && state[1, 1] == state[0, 2] && state[1, 1] != 0)
            return state[1, 1];
        return 0;
    }
    bool kill(int i,int j)
    {
        state[i, j] = turn;
        if (check() != turn)
        {
            state[i, j] = 0;
            return false;
        }
        else
        {
            state[i, j] = 0;
            return true;
        }
    }
    bool defend(int i,int j)
    {
        state[i, j] = 3 - turn;
        if (check() == 3-turn)
        {
            state[i, j] = 0;
            return true;
        }
        else
        {
            state[i, j] = 0;
            return false;
        }
    }
    bool full()
    {
        bool res = true;
        for(int i = 0; i < 3; i++)
        {
            for(int j = 0; j < 3; j++)
            {
                if (state[i, j] == 0)
                    res = false;
            }
        }
        return res;
    }
    void OnGUI()
    {
        for(int i = 0; i < 3; i++)
        {
            for(int j = 0; j < 3; j++)
            {
                if (state[i, j] == 0)
                {
                    if (GUI.Button(new Rect(i * 100, j * 100, 100, 100), ""))
                    {
                        if (check() != 0)
                            break;
                        state[i, j] = turn;
                        turn = 3 - turn;
                        if (AI == 1)
                        {
                            int done = 0;
                            for(int k = 0; k < 3; k++)
                            {
                                for(int p = 0; p < 3; p++)
                                {
                                    if (state[k, p] == 0)
                                    {
                                        if (kill(k, p))
                                        {
                                            state[k, p] = turn;
                                            done = 1;
                                        }
                                        else if (defend(k, p))
                                        {
                                            state[k, p] = turn;
                                            done = 1;
                                        }
                                    }
                                    if (done == 1)
                                        break;
                                }
                                if (done == 1)
                                    break;
                            }
                            if (done == 0)
                            {
                                for(int k = 0; k < 3; k++)
                                {
                                    for(int p = 0; p < 3; p++)
                                    {
                                        if (state[k, p] == 0)
                                        {
                                            state[k, p] = turn;
                                            done = 1;
                                            break;
                                        }
                                    }
                                    if (done == 1)
                                        break;
                                }
                            }
                            turn = 3 - turn;
                        }
                    }
                }
                else if (state[i, j] == 1)
                    GUI.Button(new Rect(i * 100, j * 100, 100, 100), "O", white);
                else if (state[i, j] == 2) 
                    GUI.Button(new Rect(i * 100, j * 100, 100, 100), "X", black);
            }
        }
        if(check()==1)
            GUI.Label(new Rect(150, 300, 200, 200), "Player1 Win");
        else if(check()==2)
            GUI.Label(new Rect(150, 300, 200, 200), "Player2 Win");
        if (GUI.Button(new Rect(350, 200, 100, 100), "Restart"))
            Restart();
        if (AI == 0)
        {
            if (GUI.Button(new Rect(450, 200, 100, 100), "AI ON"))
            {
                AI = 1;
            }
        }
        else
        {
            if (GUI.Button(new Rect(450, 200, 100, 100), "AI OFF"))
            {
                AI = 0;
            }
        }
        if (full())
        {
            GUI.Label(new Rect(150, 300, 200, 200), "Tied");
        }
    }

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

实现效果:

在这里插入图片描述
在这里插入图片描述

思路解析:

利用IMGUI来搭建简单的界面,参考了官方文档和相关博客。游戏思路是设置一个turn变量来记录当前是谁的回合(白圈是玩家一,黑叉是玩家二),如果点击在空格上则产生相应的相应图案,每帧调用检测函数,暴力检测三纵行,三横行和两对角线是否存在三色相同图案,若存在则产生游戏结果,并停止游戏,Restart用来重置游戏。
后面我又增加了AI功能,我的AI的运行逻辑是先攻击再防御,先判断是否能绝杀,若可以则直接下子,否则判断是否下一步会被对方绝杀,若会则下子防守,否则随机选取一块进行下子。井字棋的规则十分简单,我这种AI逻辑能做到把把平局,效果良好。

思考题

1.微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们,我们称这种设计为“模板方法模式”。为什么是“模板方法”模式而不是“策略模式”呢?

首先模板方法是给定一个基类模板,然后由程序员去以该模板实现各个具体类,而策略模式则是给定一系列策略,类似于 if else分支,由用户决定运行过程中的处理方式。XNA屏蔽了游戏循环的细节,用一组虚方法去让继承者完成他们,这说明微软XNA是在一个基类模板上进行子类的开发的,而不是开发出各种各样的类然后由用户进行选择。

2.将游戏对象组成树型结构,每个节点都是游戏对象(或数)。尝试解释组合模式(Composite Pattern / 一种设计模式)。使用 BroadcastMessage() 方法,向子对象发送消息。你能写出 BroadcastMessage() 的伪代码吗?
BroadcastMessage(k):
	For any child i of k:
		Inform(i);
		BroadcastMessage(i);
3.一个游戏对象用许多部件描述不同方面的特征。我们设计坦克(Tank)游戏对象不是继承于GameObject对象,而是 GameObject 添加一组行为部件(Component)。这是什么设计模式?为什么不用继承设计特殊的游戏对象?

这是装饰器模式。继承会造成高耦合,导致代码的复杂度上升,而采用组件可以降低耦合,增强内聚。

猜你喜欢

转载自blog.csdn.net/SYSU_yzt/article/details/100626441