SYSU-SSE 3D游戏编程与设计 学习笔记(1)--离散仿真引擎基础
前言
中山大学软件工程学院 3D游戏编程与设计课程学习记录博客
游戏代码: 游戏代码
简答题
- 解释游戏对象(GameObjects)和资源(Assets)的区别与联系
游戏对象是在游戏中真实存在一个物体,是可以进行选中操作的对象,而资源则是对游戏对象的一个修饰,比如一个正方体是游戏对象,但是其表面的图案等具体信息是资源来设定的。 - 下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
资源的目录组织结构基本包括Prefabs预设,resources动态加载的资源文件,Scenes场景文件,Scenes场景文件,Scripts脚本代码文件,Sounds音效文件,Textures所有的贴图等等…而游戏对象树类似于多个父子继承关系,一个游戏对象往往是包括了多个子对象。 - 编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
- 基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
- 常用事件包括 OnGUI() OnDisable() OnEnable()
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript1 : 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 Reset()
{
Debug.Log("Reset!");
}
void OnGUI()
{
Debug.Log("onGUI!");
}
void OnDisable()
{
Debug.Log("onDisable!");
}
void OnDestroy()
{
Debug.Log("onDestroy!");
}
}
- Awake():用于在游戏开始之前初始化变量或游戏状态。在脚本整个生命周期内它仅被调用一次.Awake在所有对象被初始化之后调用。
- Start():仅在Update函数第一次被调用前调用。Start在behaviour的生命周期中只被调用一次。
- FixedUpdate():固定帧更新,更新频率默认为0.02s。
- Update():正常帧更新,用于更新逻辑,每一帧都执行。FixedUpdate比较适用于物理引擎的计算,因为是跟每帧渲染有关。Update就比较适合做控制。
- LateUpdate():在所有Update函数调用后被调用,和fixedupdate一样都是每一帧都被调用执行,这可用于调整脚本执行顺序。
- OnGUI():在渲染和处理GUI事件时调用。这意味着OnGUI也是每帧执行一次。
- Reset():在用户点击检视面板的Reset按钮或者首次添加该组件时被调用。
- OnDisable():当物体被销毁时 OnDisable将被调用,并且可用于任意清理代码。脚本被卸载时,OnDisable将被调用,OnEnable在脚本被载入后调用。
- OnDestroy():当MonoBehaviour将被销毁时,这个函数被调用。OnDestroy只会在预先已经被激活的游戏物体上被调用。
- 查找脚本手册,了解GameObject,Transform,Component 对象
- 分别翻译官方对三个对象的描述(Description)
GameObject: 游戏中的每个对象都是一个游戏对象(GameObject)。然而,游戏对象(GameObjects)本身不做任何事情。它们需要特殊属性(special properties)才能成为一个角色、一种环境或者一种特殊效果。 Transform:变换(Transforms)是每个游戏对象(GameObject)的关键组件(Component)。它们决定游戏对象 (GameObject)的位置、旋转方式及缩放。 Reset:在游戏中,组件(Components)就是对象和行为的螺栓与螺母,它们是每个游戏对象 (GameObject)的功能零件。 - 描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件
table 的对象是GameObject,第一个选择框是 activeSelf属性,第二个选择框是Transform属性,第三个选择框是Mesh Filter筛网过滤器属性,第四个选择框是Box Collider属性,第五个选择框是Mesh Renderer筛网渲染器属性,第六个选择框是Default-Material属性。
- 资源预设(Prefabs)与 对象克隆 (clone)
- 预设(Prefabs)有什么好处?
预设是一个非常容易复用的类模板,可以迅速方便创建大量相同属性的对象、操作简单,代码量少,减少出错概率。 - 预设与对象克隆(clone or copy or Instantiate of Unity Object)关系?
预设可以使修改的复杂度降低,一旦需要修改所有相同属性的对象,只需要修改预设即可,所有通过预设实例化的对象都会做出相应变化。而克隆只是复制一个一模一样的对象,这个对象独立于原来的对象,在修改的过程中不会影响原有的对象,这样不方便整体改动。 - 制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
void Start()
{
Debug.Log("Start!");
GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
cube.name = "a cube";
cube.transform.position = new Vector3(0, 1, 2);
cube.transform.parent = this.transform;
}
井字棋游戏实现
游戏演示视频: 演示视频
编程内容
- 了解OnGUI()事件,提升debug能力
- 提升阅读API文档能力
- 训练数据-控制分离的编程思想
- 仅使用IMGUI构建UI编写井字棋小游戏
实现思路
- 确定屏幕中心位置
int w = Screen.width / 2;
- 放置游戏名称和重新开始按钮
GUI.Box(new Rect(w - 225, 50, 450, 500), "井字棋");
if(GUI.Button(new Rect(w - 325, 75, 100, 25), "重新开始")) Reset();
- 根据一个二维数组
matrix
来确定玩家下棋位置及下棋功能
for(int i = 0; i < 3; i++) {
for(int j = 0; j < 3; j++) {
if(matrix[i, j] == 1) GUI.Button(new Rect(w - 225 + j*Bwidth, 100 + i*Bwidth, Bwidth, Bwidth), "X");
else if(matrix[i, j] == 2) GUI.Button(new Rect(w - 225 + j*Bwidth, 100 + i*Bwidth, Bwidth, Bwidth), "O");
//当游戏结束时点击button无效果
else if(result != 0) GUI.Button(new Rect(w - 225 + j*Bwidth, 100 + i*Bwidth, Bwidth, Bwidth), "");
else {
if(GUI.Button(new Rect(w - 225 + j*Bwidth, 100 + i*Bwidth, Bwidth, Bwidth), "")) {
matrix[i, j] = 1 + player % 2;
result = Check();
player++; //辅助计算当前回合下棋的玩家
cnt++;
}
}
}
}
- 根据下的棋子数量
cnt
和Check()
函数返回的结果来判断游戏结果
if(result == 1) {
GUI.Box(new Rect(w - 325, 50, 100, 25), "玩家1(X)获胜");
}
else if(result == 2) {
GUI.Box(new Rect(w - 325, 50, 100, 25), "玩家2(O)获胜");
}
else if(cnt == 9) {
GUI.Box(new Rect(w - 325, 50, 100, 25), "平局");
}
else {
GUI.Box(new Rect(w - 325, 50, 100, 25), "正在游戏");
}
Check()
函数先判断同行或同列是否达成获胜条件,再判断两条对角线是否达成获胜条件并返回获胜玩家编号;否则返回0
for(int i = 0; i < 3; i++) {
if(matrix[i,0] == matrix[i,1] && matrix[i,1]== matrix[i,2] && matrix[i,0] != 0) return matrix[i,0];
if(matrix[0,i] == matrix[1,i] && matrix[1,i]== matrix[2,i] && matrix[0,i] != 0) return matrix[0,i];
}
if(matrix[0,0] == matrix[1,1] && matrix[1,1] ==matrix[2,2]) return matrix[1,1];
if(matrix[0,2] == matrix[1,1] && matrix[1,1] ==matrix[2,0]) return matrix[1,1];
return 0;