Unity3d--离散仿真基础

Unity3d–离散仿真基础


1.简答题

  • 解释 游戏对象(GameObjects)和 资源(Assets)的区别与联系。
    区别:游戏对象(GameObjects)是Unity中的基本对象,代表角色,道具和风景等等。它本身并不完成实际的功能,而是各种组件的容器,根据要创建的对象类型,可以添加不同的组件组合到游戏对象中。资源(Assets)包含在游戏文件中,可以是我们自定义或下载下来的素材。资源可能来自Unity外部创建的文件,例如3D模型,音频文件,图像或Unity支持的其他文件。还有一些是可以在Unity中创建的资源。
    联系:资源可以被多个游戏对象使用,有些资源可以做为模板并实例化为对象。
  • 下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
    我在Asset Store中下载了DreamForestTree项目,通过这个案例总结资源和对象组织的结构。
    资源的目录组织结构:资源结构主要包括动画,文本,场景,素材,模型,预设以及使用说明等。
    在这里插入图片描述
    对象树的层次结构:对象组织的结构主要包括摄像机包,游戏开始位置,场景布局以及文本管理等。
  • 编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
    • 基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
    • 常用事件包括 OnGUI() OnDisable() OnEnable()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour {
	//程序一开始调用
	void Awake(){
		Debug.Log ("Awake");
	}
	// Use this for initialization
	//在Awake之后调用
	void Start () {
		Debug.Log ("Start");
	}
	// Update is called once per frame
	//每帧调用一次
	void Update () {
		Debug.Log ("Update");
	}
	//每隔特定的时间更新
	void FixedUpdate(){
		Debug.Log ("FixedUpdate");
	}
	//所有对象渲染完毕调用
	void LateUpdate(){
		Debug.Log ("LateUpdate");
	}
	//对GUI的对象产生交互时调用
	void OnGUI(){
		Debug.Log ("OnGUI");
	}
	//对象不可用时调用
	void OnDisable(){
		Debug.Log ("OnDisable");
	}
	//对象可用时调用
	void OnEnable(){
		Debug.Log ("OnEnable");
	}
}

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

    • 分别翻译官方对三个对象的描述(Description)
      GameObject:The fundamental object in Unity scenes, which can represent characters, props, scenery, cameras, waypoints, and more. A GameObject’s functionality is defined by the Components attached to it.
      游戏对象(GameObject):Unity场景中的基本对象,可以表示角色,道具,风景,相机,航点等。 GameObject的功能由附加到其上的组件定义。
      Transform:The Transform component determines the Position, Rotation, and Scale of each object in the scene. Every GameObject has a Transform.
      变换组件(Transform):变换组件确定场景中每个对象的位置,旋转和缩放。 每个游戏对象都有一个变换组件。
      Component:A functional part of a GameObject. A GameObject can contain any number of components. Unity has many built-in components, and you can create your own by writing scripts that inherit from MonoBehaviour.
      组件(Component):游戏对象的功能部分。游戏对象可以包含任意数量的组件。Unity有许多内置组件,您可以通过编写从MonoBehaviour继承的脚本来创建自己的组件。
    • 描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件
      • 本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
      • 例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。
        table 的对象是 GameObject,第一个选择框是 activeSelf 属性。第二个文本框是对象名称,第三个选择框为static属性。第二行有Tag属性和Layer属性,第三行为prefabs(预设)属性。
        Transform属性包括position(位置),Rotation(旋转),Scale(比例)。
        Component对象有Transform,Mesh Filter,Box Collider,Mesh Renderer。在这里插入图片描述
    • 用 UML 图描述 三者的关系(请使用UMLet 14.1.1 stand-alone版本出图)
      在这里插入图片描述
  • 整理相关学习资料,编写简单代码验证以下技术的实现:

    • 查找对象
      通过名字查找:

    public static GameObject Find(string name)
    在这里插入图片描述
    通过标签查找单个对象:
    public static GameObject FindWithTag(string tag)
    在这里插入图片描述
    通过标签查找多个对象:
    public static GameObject[] FindGameObjectsWithTag(string tag)
    在这里插入图片描述

    • 添加子对象
      在这里插入图片描述
    • 遍历对象树
      在这里插入图片描述
    • 清除所有子对象
      在这里插入图片描述
  • 资源预设(Prefabs)与 对象克隆 (clone)

    • 预设(Prefabs)有什么好处?
      1.使对象和资源能够重复利用。
      2.相同的游戏对象可以用同一个预制来创建。
      3.对预设进行修改后,所有的游戏对象都会发生改变。
      总体而言,预设资源储存了完整储存了对象的组件和属性,相当于模板,使用预制能够方便我们创建相同的游戏对象并赋予他们相同的行为。
    • 预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?
      1.克隆游戏对象需要场景中有被克隆对象,而创建预制只需事先创建预制即可,允许场景中一开始并不存在该游戏对象。
      2.克隆出来的游戏对象并不会随着被克隆体的变化而发生变化,但是使用预制创建出来的对象会随着预制的改变而发生改变。
  • 制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
    制作table预制:
    在这里插入图片描述
    代码实例化:

table = Resources.Load (“table”);
GameObject instance = Instantiate(table);

在这里插入图片描述


2、 编程实践,小游戏

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

首先是游戏效果展示
在这里插入图片描述
共三种情况,玩家1胜,玩家2胜和平局。
下面是代码逻辑:
通过一个二维数组board来表示棋盘上每个点的状态信息,0为未点中,1为玩家1点中,2为玩家2点中。
turn表示当前到哪个玩家进行选择,1为玩家1选择,2为玩家2选择,初始为玩家1进行选择。
res表示结果,0为正在游戏中,1为玩家1获胜,2为玩家2获胜,3为平局。
在这里插入图片描述
游戏结束后需要重新开始,其逻辑是将board上每个位置的状态都重新初始化为0,代表每个位置都还未被点中。
在这里插入图片描述
在游戏中需要检查游戏是否结束,并判断其结果,井字棋获胜要求是行或列或对角线连成三个相同的棋子即获胜,所以需要从每行,每列,每条对角线来判断三个棋子是否相同,若都不相同且所有格子未被填满,则说明游戏还在进行中,若9个位置都有棋子且没有连城3个的棋子,则说明游戏是平局。最终通过board中连成三个的是1还是2来判断玩家1或玩家2获胜。

初始的Start函数逻辑与reset函数逻辑相同,直接调用reset函数即可。
在这里插入图片描述
接下来进入GUI函数,首先搭建游戏菜单界面以及字体格式。
在这里插入图片描述
玩家1为绿色圆圈,玩家2为红色叉。
接着是构建棋盘,需要用到GUI.Button函数,它会一直监听整个事件,一旦被点击则返回true执行下面的逻辑。首先创建3*3的空棋盘,若其中某个按钮被点击,且此时游戏未结束(防止游戏结束后仍可以点击棋盘),那么就将这个棋盘位置的状态从0变为此时turn的值,也就是哪个玩家点下的这个位置,接下来若此处为1,则将此处按钮换为绿色圆圈,若为2,则换为红色叉,这样就完成了游戏期盼的构建。
在这里插入图片描述
在玩家交换点击的过程中需要知道此时应该哪个玩家点击,所以设计了提示,在轮到改为玩家时,玩家右侧会出现小点。
在这里插入图片描述
接下来需要创建reset按钮,按下代表游戏重新开始。在这里插入图片描述
最后需要实时判断游戏结果,根据check函数返回不同的值来判断游戏是否结束以及结束状态。
在这里插入图片描述
完整代码如下所示

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

public class 井字棋 : MonoBehaviour {
	private int turn = 1;
	private int[,] board = new int [3, 3];
	private int res = 0;
	// Use this for initialization
	void reset(){
		turn = 1;
		res = 0;
		for (int i = 0; i < 3; ++i)
			for (int j = 0; j < 3; ++j) board [i,j] = 0;
	}

	int check(){
		int count = 0;
		for (int i = 0; i < 3; i++) 
			for (int j = 0; j < 3; j++) if (board [i, j] != 0) count++;
		for (int i=0; i<3; ++i) if (board[i,0]!=0 && board[i,0]==board[i,1] && board[i,1]==board[i,2]) return board[i,0];    			  
		for (int j=0; j<3; ++j) if (board[0,j]!=0 && board[0,j]==board[1,j] && board[1,j]==board[2,j]) return board[0,j];          
		if (board[1,1]!=0 && (board[0,0]==board[1,1] && board[1,1]==board[2,2] || board[0,2]==board[1,1] && board[1,1]==board[2,0])) return board[1,1];    
		if (count == 9) return 3;
		return 0;
	}

	void Start () {
		reset ();
	}
	
	// Update is called once per frame
	void OnGUI(){
		GUIStyle Style = new GUIStyle();  
		Style.normal.background = null;   
		Style.normal.textColor= new Color(1, 0, 0);    
		Style.fontSize = 20;
		GUIStyle Style2 = new GUIStyle ();
		Style2.normal.background = null;   
		Style2.normal.textColor= new Color(1, 0, 0);    
		Style2.fontSize = 50;
		GUIStyle Style3 = new GUIStyle();   
		Style3.normal.textColor= new Color(0, 1, 0);    
		Style3.alignment = TextAnchor.MiddleCenter;
		Style3.fontSize = 40;
		GUIStyle Style4 = new GUIStyle();   
		Style4.normal.textColor= new Color(1, 0, 0);    
		Style4.alignment = TextAnchor.MiddleCenter;
		Style4.fontSize = 40;
		GUIStyle Style5 = new GUIStyle();  
		Style5.normal.background = null;   
		Style5.normal.textColor= new Color(0, 1, 0);    
		Style5.fontSize = 20;
		GUIStyle Style6 = new GUIStyle ();
		Style6.normal.background = null;   
		Style6.normal.textColor= new Color(0, 1, 0);    
		Style6.fontSize = 50;
		GUIStyle Style7 = new GUIStyle();  
		Style7.normal.background = null;   
		Style7.normal.textColor= new Color(0, 0, 0);    
		Style7.fontSize = 20;
		GUI.Label (new Rect (400, 50, 200, 50), "Welcome To TicTacToe",Style7);
		GUI.Label (new Rect (280, 25, 100, 50), "Turns:", Style7);
		GUI.Label (new Rect (280, 55, 100, 50), "Player1", Style5);
		GUI.Label (new Rect (280, 85, 100, 50), "Player2", Style);
		for (int i = 0; i < 3; ++i)
			for (int j = 0; j < 3; ++j) {
				if (board [i,j] == 0)
				if (GUI.Button (new Rect (430+50*i, 100+50*j, 50, 50), "")&&check()==0) {
					board [i, j] = turn;
					turn = 3 - turn;
				}
				if (board [i, j] == 1) GUI.Button (new Rect (430+50*i, 100+50*j, 50, 50), "o",Style3);
				if (board [i, j] == 2) GUI.Button (new Rect (430+50*i, 100+50*j, 50, 50), "x",Style4);
			}
		if (turn == 1) GUI.Label (new Rect (350, 40, 100, 50), "·", Style6);
		else GUI.Label (new Rect (350, 70, 100, 50), "·", Style2);
		if (GUI.Button (new Rect (280, 200, 100, 50), "Reset")) reset ();
		res = check ();
		if(res==1) GUI.Label (new Rect (280, 150, 100, 50), "Player 1 Win",Style7);
		else if(res==2) GUI.Label (new Rect (280, 150, 100, 50), "Player 2 Win",Style7);
		else if(res==3) GUI.Label (new Rect (280, 150, 100, 50), "Tie",Style7);
		else GUI.Label (new Rect (280, 150, 100, 50), "Playing...",Style7);
	}
}

在界面中我创建了一个平面,并调整摄像头角度使平面铺满整个摄像头,然后将写好的C#文件挂载在平面上,就完成了整个游戏的设计。

完整工程见github地址:井字棋

参考博客:Unity编程实战——井字棋 TicTacToe


3.思考题

  • 微软 XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们,我们称这种设计为“模板方法模式”。
    • 为什么是“模板方法”模式而不是“策略模式”呢?
      模板方法模式:定义一个算法流程,将一些特定步骤的具体实现、延迟到子类。使得可以在不改变 算法流程的情况下,通过不同的子类、来实现“定制”流程中的特定的步骤。
      策略模式:策略模式是使用委托改变整个算法,使不同的算法可以被相互替换,而不影响客户端的使用。
      XNA的game基类,屏蔽了游戏循环的细节,给了用户一个编程模板,等待用户实现,而不是给出现成的算法,所以是模板方法模式而不是策略模式。
  • 将游戏对象组成树型结构,每个节点都是游戏对象(或数)。
    • 尝试解释组合模式(Composite Pattern / 一种设计模式)。
      组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
    • 使用 BroadcastMessage() 方法,向子对象发送消息。你能写出 BroadcastMessage() 的伪代码吗?
    BroadcastMessage(child,message){
        Send message to child;
        BroadcastMessage(child->left,message);
        BroadcastMessage(child->right,messge);
    }
  • 一个游戏对象用许多部件描述不同方面的特征。我们设计坦克(Tank)游戏对象不是继承于GameObject对象,而是 GameObject添加一组行为部件(Component)。
    • 这是什么设计模式?
      装饰器模式。
    • 为什么不用继承设计特殊的游戏对象?
      装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。而继承则限制了系统的灵活性,增强了耦合,因为继承的子类必须拥有父类的所有方法和属性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值