3D-02-离散仿真引擎基础与井字棋

本文是3D游戏编程与设计第二次作业的博客,分为简答题部分与编程实践部分

一、简答题

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

游戏对象(GameObjects):游戏场景中的事物,可以创建为 Empty、3D物体、摄像机等不同种类,是游戏资源的容器,本身不具有功能,在被赋予资源后成为具有功能的对象
游戏资源(Assets):装饰游戏对象、配置游戏对象的素材与脚本,或是预先设置好的对象预制。

2.下载几个游戏案例,分别总结资源、对象组织的结构

资源按照类别进行组织,如游戏角色(Characters)、环境(Environment)、道具(Utility)等等,每个类别下有自己的材料(Materials)、脚本(Scripts)、动画(Animator)、预制(Prefabs)等等
对象以树进行组织,每个对象具有自己的子对象,如Car下有Wheelhub、light、body等,Wheelhub内又有rightWheel、leftWheel等等。

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

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

Monobehaviour触发条件
Awake()在加载脚本实例时调用
Start()在首次调用任何 Update 方法之前启用脚本时,在帧上调用 Start
Update()如果启用了 MonoBehaviour,则每帧调用 Update
FixedUpdate()用于物理计算且独立于帧率的 MonoBehaviour.FixedUpdate 消息
LateUpdate()如果启用了 Behaviour,则每帧调用 LateUpdate
OnGUI()系统调用 OnGUI 来渲染和处理 GUI 事件
OnDisable()该函数在行为被禁用时调用
OnEnable()该函数在对象变为启用和激活状态时调用

引用自unity中文网

用来验证的代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class test_Monobeh : MonoBehaviour {
    void Awake() {
        Debug.Log ("Awake");
    }
 
    void Start () {
        Debug.Log ("Start");
    }
 
    void Update () {
        Debug.Log ("Update");
    }
 
    void FixedUpdate() {
        Debug.Log ("FixedUpdate");
    }
 
    void LateUpdate() {
        Debug.Log ("LateUpdate");
    }
 
    void OnGUI() {
        Debug.Log ("OnGUI");
    }
 
    void OnDisable() {
        Debug.Log ("OnDisable");
    }
 
    void OnEnable() {
        Debug.Log ("OnEnable");
    }
}

验证结果
在这里插入图片描述

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

4.1.分别翻译官方对三个对象的描述(Description)

Description:
GameObject: Base class for all entities in Unity Scenes.
Transform: Position, rotation and scale of an object.
Component: Base class for everything attached to GameObjects.

翻译
GameObject:Unity场景中所有实体的基类
Transform:一个对象的位置,旋转和大小
Component:所有绑定到GameObjects的事物的基类

4.2.描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件

  • 本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
  • 例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。
    在这里插入图片描述
    对象的属性:依次为activeSelf、name、Static、Tag、Layer、Prefab
    Transform的属性:依次为Position(X、Y、Z)、Rotation(X、Y、Z)、Scale(X、Y、Z)
    部件:Transform、Cube、Box Collider等

4.3.用 UML 图描述 三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图)

因为电脑java环境版本为12.0.2,与UMLet 14.1.1所需1.6.0不符,所以用了UMLet 14.3.0 stand-alone版本绘制UML图
在这里插入图片描述

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

5.1.预设(Prefabs)有什么好处?

当某些配置好的游戏对象和资源会被重复使用时,使用预设可以省去很多重复性的工作(如创建、修改等),提高效率。

5.2.预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?

联系:都是创建新对象的方式
区别:修改预设会使其创建的对象一起改变,而修改本体不会使克隆体发生变化。

5.3.制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象

首先创建五个(3D Object)Cube对象,使其中的四个成为另一个的子对象,然后调整Transform中的属性,得到对象如图
在这里插入图片描述
拖入Assets成为预制,命名为table
在这里插入图片描述
删除遗留在场景中的table对象,创建Empty对象命名为init,将脚本prefab_init添加到init上,然后将table预制拖入init属性中脚本prefab_init的Prefab中
在这里插入图片描述
prefab_init 代码如下

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

public class prefab_init : MonoBehaviour
{
    public Transform prefab;
    void Start()
    {
        Instantiate(prefab, new Vector3(0, 0, 0), Quaternion.identity);
    }
}

效果如下
在这里插入图片描述

二、编程实践

1.任务内容

  • 游戏内容: 井字棋
  • 技术限制: 仅允许使用 IMGUI 构建 UI
  • 作业目的:
    a)了解 OnGUI() 事件,提升 debug 能力
    b)提升阅读 API 文档能力

2.任务分析

阅读IMGUI文档后,发现一大要点就是在GUI事件后执行OnGUI()函数中的代码,所以想到用一个二维数组记录棋盘的信息,依据二维数组中的信息更新棋盘的状态,然后在每次GUI事件后,判断当前棋局的状态以及更新二维数组以响应。因此游戏实现的重点就是OnGUI()函数以及判断棋局信息的函数check():
OnGUI()函数中,创建一个内容的主容器并加上游戏标题,然后添加具有重置游戏功能的Reset按钮,因为该功能不受当前棋局的干扰,然后进行棋局状态的判断与UI显示,之后便是关键性的棋盘创建,我选择利用按钮作为棋子落点,先将有棋子的按钮加入棋盘,然后检测无棋子按钮的状态判断下一落点,在落子后更新数组信息。
check()函数实现较为容易,因为我们将棋盘信息储存在数组中,所以只需要获取数组中的信息就可以,依次检测横线、竖线、斜线是否出现三连子判断是否有玩家胜利,有则返回胜利玩家的信息,无则通过棋子是否落慢棋盘检测棋局是否结束,返回状态信息。

3. 代码展示

Unity版本:2020.1.6f1c1
gitee传送门

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

public class tictactoe : MonoBehaviour {
    private int turn = 1;//player1 is 1,player2 is 2
    private int[,] chessBoard = new int[3,3];//0 for blank,1 for p1,2 for p2
    private float box_len = 100;
    private float scrmid = Screen.width / 2;
    public GUIStyle pushed_box;

    void Start() {
        reset();
    }

    void OnGUI() {      
        GUI.Box(new Rect(scrmid - 160, 110, 320, 500), "Tic-Tac-Toe");// Main container
        if (GUI.Button(new Rect(scrmid - 50, 520, 100, 40), new GUIContent("Reset", "Click to restart"))) 
            reset();

        int situation = check();
        display(situation);
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (chessBoard[i,j] == 1) {
                    GUI.Button(new Rect(scrmid - 1.5f * box_len + i * box_len, 200 + j * box_len, box_len, box_len), "O", pushed_box);
                }
                else if (chessBoard[i,j] == 2) {
                    GUI.Button(new Rect(scrmid - 1.5f * box_len + i * box_len, 200 + j * box_len, box_len, box_len), "X", pushed_box);
                }   
                if(GUI.Button(new Rect(scrmid - 1.5f * box_len + i * box_len, 200 + j * box_len, box_len, box_len), "")) { 
                    if (situation == 0) {
                        if (turn == 1) {
                            chessBoard[i,j] = turn;
                            turn = 2;
                        }
                        else if(turn == 2){
                            chessBoard[i,j] = turn;
                            turn = 1;
                        }
                    }  
                }
            }
        }
        
    }
 
    /*check the situation*/
    int check() {   
        //check x-axis  
        for (int i = 0; i < 3; i++) {
            if (chessBoard[i,0] != 0 && chessBoard[i,0] == chessBoard[i,1] && chessBoard[i,1] == chessBoard[i,2]) {
                return chessBoard[i,0];
            }
        }
        //check y-axis
        for (int i = 0; i < 3; i++) {
            if (chessBoard[0,i] != 0 && chessBoard[0,i] == chessBoard[1,i] && chessBoard[1,i] == chessBoard[2,i]) {
                return chessBoard[0,i];
            }
        }
        //check slash
        if (chessBoard[1,1] != 0 && chessBoard[0,0]== chessBoard[1,1] && chessBoard[2,2] == chessBoard[1,1] ||
            chessBoard[0,2] == chessBoard[1,1] && chessBoard[2,0] == chessBoard[1,1]) {
            return chessBoard[1,1];
        }
        //check end
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; ++j) {
                if (chessBoard[i,j] == 0) return 0;
            }
        }
        //end but no one win,return a tie
        return 3;
    }
    /*Reset the game*/
    void reset() {
        turn = 1;//player1 first
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                chessBoard[i,j] = 0;//each board is blank
            }
        }
    }
    /*display the situation*/
    void display(int situation) {
        if (situation == 0) {
            if (turn == 1)
                GUI.Box(new Rect(scrmid - 50, 160, 100, 35), "Player1's turn");
            else if(turn == 2)
                GUI.Box(new Rect(scrmid - 50, 160, 100, 35), "Player2's turn");
        }
        else if (situation == 1) {
            GUI.Box(new Rect(scrmid - 50, 160, 100, 35), "Player1 win!");
        }
        else if (situation == 2) {
            GUI.Box(new Rect(scrmid - 50, 160, 100, 35), "Player2 win!");
        }
        else if (situation == 3) {
            GUI.Box(new Rect(scrmid - 50, 160, 100, 35), "A tie!");
        }
    }
    
}

4. 成果展示

加载方法:与简答题5.3的方法相同,创建Empty后将脚本tictactoe挂载在上面,需要设置代码中出现的GUIStyle,我调整了字体大小(Font Size)和位置(Content Offset)
在这里插入图片描述
游戏中:
在这里插入图片描述

一方胜利:
在这里插入图片描述

三、思考题

  1. 微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们,我们称这种设计为“模板方法模式”。
  • 为什么是“模板方法”模式而不是“策略模式”呢?
    模板方法是定义一个算法骨架后,将一些步骤的实现延迟到子类中,使子类在不改变算法结构的情况下定义算法的某些步骤;策略模式是实现与封装多个算法,使它们在使用时可以相互替换,与这种设计的思想不一致。
  1. 将游戏对象组成树型结构,每个节点都是游戏对象(或数)。
  • 尝试解释组合模式(Composite Pattern / 一种设计模式)。
    用于将一组相似的对象当作一个单一的对象,以实现对一组对象的统一处理。将对象以"部分-整体"的层次结构组合成树形结构,在高层中提供可以统一修改组中对象的函数的接口。
  • 使用 BroadcastMessage() 方法,向子对象发送消息。你能写出 BroadcastMessage() 的伪代码吗?
void BoradcastMessage(string message){
    foreach child in this{
        child.getMessage(message);
    }
}
  1. 一个游戏对象用许多部件描述不同方面的特征。我们设计坦克(Tank)游戏对象不是继承于GameObject对象,而是 GameObject 添加一组行为部件(Component)。
  • 这是什么设计模式?
    装饰器模式
  • 为什么不用继承设计特殊的游戏对象?
  1. 难以用继承机制描述各类事物
  2. 继承、多态这种机制会导致对象行为变得复杂
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值