这是本学期3D游戏编程与设计的第一次作业,内容主要是熟悉Unity3D的基本原理和使用方法,以及C#的编程语法。
在Unity的官方介绍文档当中,可以找到对Assets的定义如下:
An Asset is a representation of any item you can use in your game or Project. An Asset may come from a file created outside of Unity, such as a 3D Model, an audio file, an image, or any of the other file types that Unity supports. There are also some Asset types that you can create in Unity, such as a ProBuilder Mesh, an Animator Controller, an Audio Mixer, or a Render Texture.
资源是在游戏设计过程中你可以使用的一切物体和属性。Unity中有很多中资源, GameObject、Component理论上也都是资源,但我们通常说的资源指的是:材质、网格、动画和给一个物体插入一段代码等等。
而对GameObjects的定义如下:
GameObjects are the fundamental objects in Unity that represent characters, props and scenery. They do not accomplish much in themselves but they act as containers for Components, which implement the real functionality.
For example, a Light object is created by attaching a Light component to a GameObject.
A GameObject always has a Transform component attached (to represent position and orientation) and it is not possible to remove this. The other components that give the object its functionality can be added from the editor’s Component menu or from a script. There are also many useful pre-constructed objects (primitive shapes, Cameras, etc) available on the GameObject > 3D Object menu, see Primitive Objects.
游戏对象则是游戏当中呢的玩家,物体或者环境等等,它主要是代表一个角色。游戏对象可以由很多的资源组成,比如在meterial可以更改物体的颜色属性,还可以增加代码脚本,增加动画声音等等。
资源可以被多个对象使用,只需要把要用的资源呢拖到对应游戏对象处就行了,资源作为模版,可以实例化游戏中具体的对象。
一个用Unity3d做的游戏项目中会包含一个Assets文件夹。资源文件夹的内容呈现在项目视图。这里存放着游戏的所有资源,在资源文件夹中,通常包括材质Materials、图片Images、预制件Prefabs、场景Scences、声音Sounds、脚本Scripts等等,根据游戏具体的需求和设计稍有不同,在这些文件夹下可以继续进行划分。
游戏对象一般包括玩家,敌人,环境,摄像机等虚拟父类,对象按照用途进行组织,执行相同操作的对象放在一起。在unity3d的操作界面左边拖动不同的游戏对象,可以调整它们直接的层次关系,使得一个成为另一个的子类或父类,子类会继承父类的属性。
基本行为包括 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
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");
}
void OnGUI()
{
Debug.Log("OnGUI");
}
void OnDisable()
{
Debug.Log("OnDisable");
}
void OnEnable()
{
Debug.Log("OnEnable");
}
}
GameObject :GameObjects are the fundamental objects in Unity that represent characters, props and scenery. They do not accomplish much in themselves but they act as containers for Components, which implement the real functionality.
翻译:游戏对象是Unity中表示游戏角色,游戏道具和游戏场景的基本对象。它们本身并不能完成很多功能,但是它们充当了那些给予他们实体功能的组件的容器。
Transform :The Transform component determines the Position, Rotation, and Scale of each object in the scene. Every GameObject has a Transform.
翻译:转换决定游戏对象的位置,以及它是如何旋转和缩放的。每个游戏对象都有一个转换。
Component :Components are the nuts & bolts of objects and behaviors in a game. They are the functional pieces of every GameObject.
翻译: 组件是游戏中对象和行为的细节,它是每个游戏对象的功能部分。
描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件.
本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。
查找对象
public static GameObject Find(string name);
public static GameObject FindWithTag(string tag);
public static GameObject[] FindGameObjectsWithTag(string tag);
添加子对象
public static GameObject CreatePrimitive(PrimitiveType type);
Creates a game object with a primitive mesh renderer and appropriate collider.
遍历对象树
foreach (Transform child in transform) {
Debug.Log(child.gameObject.name);
}
foreach (Transform child in transform) {
Destroy(child.gameObject);
}
void Start () {
GameObject anotherTable = (GameObject)Instantiate(table.gameObject);
}
“Immediate Mode” GUI系统(也称为IMGUI)是Unity的一个独立于图形化视图的代码特性系统,完全通过代码来实现基于游戏对象的UI设计和其他功能的设计。IMGUI是一个代码驱动的GUI系统,主要用于作为程序员的工具。它是由对任何实现它的脚本的OnGUI函数的调用驱动的。
要创建IMGUI元素,必须编写代码进入一个名为OnGUI的特殊函数。代码需要在每一帧显示这个接口并绘制到屏幕上。除了你的代码附载的那个游戏对象以外,没有别的一直存在的或是和代码所依附的对象有继承关系的的对象了。
IMGUI允许您使用代码创建各种各样的功能GUI。与其创建gameobject(游戏对象),手动定位它们,然后编写一个脚本来处理其功能,您可以用几行代码立即完成所有事情。代码生成GUI控件,这些控件通过一个函数调用来绘制和处理。
/* GUI.Label example */
using UnityEngine;
using System.Collections;
public class GUITest : MonoBehaviour
{
void OnGUI ()
{
GUI.Label (new Rect (25, 25, 100, 30), "Label");
}
}
The Label is non-interactive. It is for display only. It cannot be clicked or otherwise moved. It is best for displaying information only.
/* GUI.Button example */
using UnityEngine;
using System.Collections;
public class GUITest : MonoBehaviour
{
void OnGUI ()
{
if (GUI.Button (new Rect (25, 25, 100, 30), "Button"))
{
// This code is executed when the Button is clicked
}
}
}
In UnityGUI, Buttons will return true when they are clicked. To execute some code when a Button is clicked, you wrap the the GUI.Button function in an if statement. Inside the if statement is the code that will be executed when the Button is clicked.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Chess : MonoBehaviour
{
private int[,] chessBoard = new int[3, 3];//初始化井字棋棋盘
private int turn = 1;
// Use this for initialization
void Start()
{
Reset();
}
// Update is called once per frame
void Update()
{
}
void Reset()
{
turn = 1;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
chessBoard[i, j] = 0;
}
}
}
void OnGUI()
{
// GUIStyle design.
GUIStyle Style = new GUIStyle();
Style.normal.background = null;
Style.normal.textColor = new Color(0, 5, 1);
Style.fontSize = 35;
Style.fontStyle = FontStyle.Bold;
//Type (Position, Content)
//Rect() defines four properties: left-most position, top-most position, total width, total height.
//You can use the Screen.width and Screen.height properties to get the total dimensions of the screen space available in the player.
GUI.Button(new Rect(Screen.width / 2 - 75, Screen.height / 2 - 250, 150, 40), "Tic Tac Toe");
if (GUI.Button(new Rect(Screen.width / 2 - 75, Screen.height / 2 -200, 150, 50), "RESET GAME"))//displays a Box Control with the header text “RESET GAME”.
{
Reset();
}
//First judge the result.
int State = check();
//The second argument for a GUI Control is the actual content to be displayed with the Control. Most often you will want to display some text or an image on your Control.
if (State == 2)
{
GUI.Label(new Rect(Screen.width / 2 - 60, Screen.height / 2 +50, 50, 50), "X Win!", Style);
}
else if (State == 1)
{
GUI.Label(new Rect(Screen.width / 2 - 60, Screen.height / 2 +50, 50, 50), "O Win!", Style);
}
else if (State == 0)
{
GUI.Label(new Rect(Screen.width / 2 - 60, Screen.height / 2 +50, 50, 50), "Equally", Style);
}
//The Game is not over,continue.
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
//If the number of the chessboard is 1, it represents O, else 2 represents X.
if (chessBoard[i, j] == 1)
{
GUI.Button(new Rect(Screen.width / 2 - 75 + 50 * i, Screen.height / 2 - 130 + 50 * j, 50, 50), "O");
}
else if (chessBoard[i, j] == 2)
{
GUI.Button(new Rect(Screen.width / 2 - 75 + 50 * i, Screen.height / 2 - 130 + 50 * j, 50, 50), "X");
}
//Click operation.
if (GUI.Button(new Rect(Screen.width / 2 - 75 +50 * i, Screen.height / 2 - 130 + 50 * j, 50, 50), ""))
{
if (State == 3)
{
if (turn == 1)
{
chessBoard[i, j] = 1;
}
else if (turn == -1)
{
chessBoard[i, j] = 2;
}
turn = -turn;
}
}
}
}
}
int check()
{
//Check the rows.
for (int i = 0; i < 3; i++)
{
if (chessBoard[i, 0] == chessBoard[i, 1] && chessBoard[i, 0] == chessBoard[i, 2] && chessBoard[i, 0] != 0)
{
return chessBoard[i, 0]; //1
}
}
//Check the columns.
for (int j = 0; j < 3; j++)
{
if (chessBoard[0, j] == chessBoard[1, j] && chessBoard[0, j] == chessBoard[2, j] && chessBoard[0, j] != 0)
{
return chessBoard[0, j]; //2
}
}
//Check the diagonals.
if (chessBoard[0, 0] == chessBoard[1, 1] && chessBoard[0, 0] == chessBoard[2, 2] && chessBoard[0, 0] != 0) return chessBoard[0, 0];
if (chessBoard[0, 2] == chessBoard[1, 1] && chessBoard[0, 2] == chessBoard[2, 0] && chessBoard[0, 2] != 0) return chessBoard[0, 2];
//Cteate count to find out who win the game.
//If count == 9, then nobody win the game, the game go equally.
int count = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (chessBoard[i, j] != 0)// some player put chess on that button.
{
count++;
}
}
}
if (count == 9)
{
return 0;
}
return 3; //3
}
}
Github代码传送门
为什么是“模板方法”模式而不是“策略模式”呢?
模版方法模式:
定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义改算法的某些特定步骤。
策略模式:
策略模式它定义了算法家族,分别封装起来,让它们之间可以互相替换。此模式让算法的变化,不会影响到使用算法的客户。
根据上述对模版方法模式和策略模式的定义我们可以看出,这里微软XNA 引擎的 Game 对象屏蔽了游戏循环的细节,并使用一组虚方法让继承者完成它们的核心思想不在于分离用户和算法,而是模版方法模式的核心——定义好算法的框架,用子类去处理不同的方法实现,微软 XNA 引擎的 Game 对象使用虚方法让子类完成游戏循环的细节。
组合模式:
组合模式(Composite Pattern),又叫部分整体模式,是用于把一组相似的对象当作一个单一的对象。组合模式依据树形结构来组合对象,用来表示部分以及整体层次。这种类型的设计模式属于结构型模式,它创建了对象组的树形结构。
这种模式创建了一个包含自己对象组的类。该类提供了修改相同对象组的方式。
父对象:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class father : MonoBehaviour {
void Start () {
this.BroadcastMessage("test");
}
}
子对象:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class child : MonoBehaviour {
void test(){
Debug.Log ("child");
}
}
将两个脚本分别拖动到父对象和子对象的game object上运行。BroadcastMessage的伪代码为:
public void BroadcastMessage(string bm){
for each children
call its method named bm
}
装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。
这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。
因为特殊的游戏对象意味着具有很强的灵活性。它需要在保证完成基础对象完整性的前提下,提供额外的功能,动态增加功能,而这恰好符合装饰器模式的定义。如果使用继承的话,则会产生大量的类,不便于管理,代码的可移植性也更差。