3D游戏编程与设计-Unity ch02

简答题

一、游戏对象(GameObjects)和资源(Assets)的区别与联系

  • 游戏对象(GameObjects)是能容纳实际实现功能的组件,是Unity中代表人物、场景或道具的基本对象,简单来讲GameObjects也就是场景(Scene)里所有实体的基类,组成了完整的游戏场景。游戏对象只充当组件的容器但本身不做任何事情,需要被附加上特殊属性才能成为一个具体的角色、环境或效果;

  • 资源是指在Unity项目中用到的资源文件,可以是在Unity之外创建的文件也可以是在Unity中创建的资源,如3D模型、音频文件、图像、脚本渲染纹理等;

  • GameObjects与Assets的区别在于GameObjects是我们在游戏运行中真实存在的可操作的游戏对象,而Assets是创作游戏时为实现对象的修饰而添加进工作区的资源、部件,比如一个方块是游戏对象GameObject,而方块表面的图案纹理和效果等就是由附加在方块对象上的资源Assets决定的;

  • 二者的联系是Assets可以作为模板,实例化成具体的GameObject,也可以作为GameObject中的某种属性,可以被多个GameObject同时使用。也就是说资源可能并没有在游戏场景中使用,而游戏对象是游戏运行时存在可见的,包括角色、道具、灯光和摄像头等,但不包括component,即背后运行的逻辑系统(脚本),从这个层面上讲,GameObject可以认为是Assets的子集。

二、分析游戏案例

  以切水果小游戏CutFruit为分析案例:
CutFruit 截图   该CutFruit游戏的资源文件夹Assets如下图所示,采用树状结构,分为3个子文件夹:
AssetsFiles

  子文件夹Res的结构如下图所示:
Res

  子文件夹Scenes和Scripts的结构如下:
Scenes_Scripts

  可见该游戏的资源文件夹将作用相似或性质相似的资源放在同一个文件夹中,整个文件夹呈树状结构,下一级文件夹和上一级文件夹是继承关系。游戏对象文件夹的结构也与此类似,:
ObjectWorld

ObjectUI

  图中的父对象Canvas有LifeText和SourceText两个子对象,父对象GameOverImage有子对象GameOverText和RestartGameButton,而RestartGameButton对象还有自己的子对象,整个游戏对象文件夹也呈树状结构,父对象和子对象之间是继承关系,任一级别的一个父对象都由其所有的子对象组成。

三、使用debug语句验证MonoBehaviour基本行为或事件触发条件

  基本行为包括Awake(),Start(),Update(),FixedUpdate(),LateUpdate()等,常用的事件有OnGUI(),OnDisable(),OnEnable()等。使用debug语句验证某行为触发条件可采用Debug.Log("xxx");语句。编写代码如下:

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

public class testBeh : MonoBehaviour {
    void Awake(){
        Debug.Log("Awake triggered");
    }

    void Start(){
        Debug.Log("Start triggered");
    }

    void Update(){
        Debug.Log("Update triggered");
    }

    void FixedUpdate(){
        Debug.Log("FixedUpdate triggered");
    }

    void LateUpdate(){
        Debug.Log("LateUpdate triggered");
    }

    void OnGUI(){
        Debug.Log("OnGUI triggered");
    }

    void OnDisable(){
        Debug.Log("OnDisable triggered");
    }

    void OnEnable(){
        Debug.Log("OnEnable triggered");
    }
}

  运行结果如下:
runDebug

  图中MonoBehaviour基本行为或事件的触发顺序是Awake -> OnEnable -> Start -> FixedUpdate -> Update -> LateUpdate -> OnGUI -> OnDisable -> OnDestroy ,其中:

Awake用于在所有对象初始化完成后、游戏开始之前初始化变量或游戏状态且仅被调用一次;
OnEnable在启用对象后立即被调用,特殊情况是对象处于非活跃状态则不调用OnEnable;
Start在Update函数第一次被调用前调用,且仅被调用一次;
FixedUpdate是固定帧更新,通常比更新更频繁被调用,更新频率默认是0.02s;
Update是正常帧更新,用于在每一帧更新逻辑,但在处理Rigidbody时用FixedUpdate代替;
LateUpdate在所有Update函数调用后被调用,每一帧执行一次,LateUpdate开始时更新中
执行的任何计算都将完成;
OnGUI用于响应GUI事件,每帧都多次被调用,处理顺序是Layout和Repaint>每个输入事件的
布局和键盘/鼠标事件;
OnDisable在行为被禁用或不活动时被调用。

四、了解GameObject、Transform、Component对象

  1. 翻译官方对三个对象的描述

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中表示角色、道具和场景的基本对象。它们本身并没有完成太多工作,但它们充当组件的容器,组件实现了真正的功能。

The Transform component determines the Position, Rotation, and Scale of
each object in the scene. Every GameObject has a Transform.

  “变换”组件确定场景中每个对象的位置、旋转和缩放。每个游戏对象都有一个转换。

Components are the nuts & bolts of objects and behaviors in a game. They
are the functional pieces of every GameObject.

   组件是游戏中对象和行为的具体细节。它们是每个游戏对象的功能部件。

  1. 描述下图table对象(实体)的属性、Transform属性及部件
    table
       table的对象是GameObject,第一个选择框是activeSelf属性,后面下拉栏是isStatic属性,后面是Tag、Layer、Transform属性,Transform属性中Position和Rotation在XYZ三个坐标轴上的值均为0,Scale在XYZ三个坐标轴上的值均为1。
      其中activeSelf属性表示GameObject的本地活动状态,isStatic属性用于指定游戏对象是否为静态,Tag是游戏对象的标签,Layer属性指定游戏对象的所在图层(在[0,31]范围内),Transform属性表示附加到该GameObject上的转换。
      table的部件有Mesh Filter、Box Collider、Mesh Renderer。

  2. 用UML图描述GameObject、Transform、Component的关系
      三者的关系如图所示:
    UML

五、资源预设(Prefabs)与对象克隆(clone)

  1. 预设(Prefabs)的好处
      预设可以实例化出具有相同属性的对象,并且可以将某一对象上发生的修改同步到其他所有副本对象上,所以预设有利于资源的重用,可以有效避免多次重复构建相同的对象如NPC、道具、场景等。
  2. 预设与对象克隆(clone/copy/instantiate of Unity Object)的关系
      当利用预设来创建一个新对象时,这个对象本质上相当于由预设克隆而来的,但对象克隆产生的实例都是完全独立的个体,之间不会互相影响;而预设的所有实例是紧密绑定联系的,对预设进行修改会作用到该预设的所有实例上。
  3. 制作table预制并通过代码将其实例成游戏对象
      编写代码如下:
public class InitBeh : MonoBehaviour {
    public GameObject table;

    void Awake(){
        //Debug.Log("Init Awake");
    }

    //Use this for initialization
    void Start(){
        Debug.Log("Init Start");

        //将table预设实例化为游戏对象
        GameObject table2=(GameObject)Instantiate(table.gameObject);
        table2.name="newTable";
        table2.transform.position=new Vector3(0,UnityEngine.Random.Range(0,5),0);
        table2.transform.parent=this.transform;
    }

    //Update is called once per frame
    void Update(){
        Debug.Log("Init Update");
    }
}

编程题

  基于IMGUI构建UI编写井字棋小游戏,完整代码如下:

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

public class initBeh : MonoBehaviour {
    private int turn = 1;
    //记录轮到谁:turn=1为○,turn==2为×
    int[][] board = new int[3][] { new int[3], new int[3], new int[3] };

    void Start() {
        Debug.Log("game start");
        reset();
    }

    //重新开局
    void reset() {
        turn = 1;
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                board[i][j] = 0;
            }
        }
        Debug.Log("game reset");
    }
 
    //检查胜利条件
    int check() {
        //横线bingo
        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];
            }
        }
        //纵线bingo
        for (int i = 0; i < 3; i++) {
            if (board[0][i] != 0 && board[0][i] == board[1][i] && board[1][i] == board[2][i]) {
                return board[0][i];
            }
        }
        //斜线bingo
        if (board[1][1] != 0 &&
            board[0][0]== board[1][1] && board[2][2] == board[1][1] ||
            board[0][2] == board[1][1] && board[2][0] == board[1][1]) {
            return board[1][1];
        }
        //没有人胜利 对局继续
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                if (board[i][j] == 0) return 0;
            }
        }
        //平局
        return 3;
    }

    //显示对局情况
    void display_result(int res) {
        int m = Screen.width / 2;
        if (res == 0) {
            GUI.Box(new Rect(m - 50, 165, 100, 35), "PLAY!");
        }
        else if (res == 1) {
            GUI.Box(new Rect(m - 50, 165, 100, 35), "O WIN!");
        }
        else if (res == 2) {
            GUI.Box(new Rect(m - 50, 165, 100, 35), "X WIN!");
        }
        else if (res == 3) {
            GUI.Box(new Rect(m - 50, 165, 100, 35), "平局!");
        }
    }

    void OnGUI() {
        int m = Screen.width / 2;
        GUI.Box(new Rect(m - 150, 130, 300, 420), "井字棋");
        /*如果reset被点击则重开*/
        if (GUI.Button(new Rect(m - 50, 500, 100, 35), new GUIContent("Reset", "点击重新开始"))) reset();
        GUI.Label(new Rect(m - 50, 535, 100, 35), GUI.tooltip);
        int res = check();
        for (int i = 0; i < 3; ++i) {
            for (int j = 0; j < 3; ++j) {
                if (board[i][j] == 1) {
                    GUI.Button(new Rect(m - 150 + i * 100, 200 + j * 100, 100, 100), "O");
                }
                else if (board[i][j] == 2) {
                    GUI.Button(new Rect(m - 150 + i * 100, 200 + j * 100, 100, 100), "X");
                }   
                if(GUI.Button(new Rect(m - 150 + i * 100, 200 + j * 100, 100, 100), "")) { 
                    if (res == 0) {
                        if (turn == 1) {
                            board[i][j] = turn;
                            turn = 2;
                        }
                        else {
                            board[i][j] = turn;
                            turn = 1;
                        }
                    }  
                }
            }
        }
        display_result(res);
    }
}

  游戏过程截图如下:
tictactoe

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值