SYSU-SSE 3D游戏编程与设计 学习笔记(1)--离散仿真引擎基础

前言

中山大学软件工程学院 3D游戏编程与设计课程学习记录博客
游戏代码: 游戏代码

简答题

  1. 解释游戏对象(GameObjects)和资源(Assets)的区别与联系
    游戏对象是在游戏中真实存在一个物体,是可以进行选中操作的对象,而资源则是对游戏对象的一个修饰,比如一个正方体是游戏对象,但是其表面的图案等具体信息是资源来设定的。
  2. 下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
    资源的目录组织结构基本包括Prefabs预设,resources动态加载的资源文件,Scenes场景文件,Scenes场景文件,Scripts脚本代码文件,Sounds音效文件,Textures所有的贴图等等…而游戏对象树类似于多个父子继承关系,一个游戏对象往往是包括了多个子对象。
  3. 编写一个代码,使用 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只会在预先已经被激活的游戏物体上被调用。
  1. 查找脚本手册,了解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属性。
  1. 资源预设(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++;
                }
            }
        }
    }
  • 根据下的棋子数量cntCheck()函数返回的结果来判断游戏结果
    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;
posted @ 2022-10-08 23:51  joshf  阅读(157)  评论(0编辑  收藏  举报