一、简答题
1、解释 游戏对象(GameObjects) 和 资源(Assets)的区别与联系。
游戏对象(GameObjects)是Unity中最基本的实体,它们代表了在游戏世界中的物体,可以具有位置、旋转、缩放等变换属性,并且可以附加各种组件来实现不同的功能和行为。
资源(Assets)是指在游戏开发中使用的各种素材和数据文件,如模型、纹理、音频、脚本等。资源可以被游戏对象使用或引用,它们被存储在Unity项目的资源文件夹中,并且可以在游戏运行时加载和实例化为游戏对象。
区别:
- 游戏对象是在运行时存在的实体,代表了游戏世界中的物体,而资源是在开发阶段创建和管理的文件,用于提供游戏所需的各种素材和数据。
- 游戏对象可以通过引用资源来获取所需的图形、声音、脚本等内容,资源可以被多个游戏对象共享和重用。
资源和游戏对象之间的联系在于,游戏对象可以使用资源来获取所需的属性、图形、声音等内容,资源可以被实例化为游戏对象并在游戏中使用。
2、下载几个游戏案例,分别总结资源、对象组织的结构(指资源的目录组织结构与游戏对象树的层次结构)
下面是下载并研究了三个游戏案例的资源组织结构和游戏对象树的层次结构的总结:
(1)游戏案例:冒险游戏
- 资源组织结构:文件夹包含模型文件夹、纹理文件夹、音频文件夹、脚本文件夹等。
- 游戏对象树:主角对象包含身体对象、武器对象等,敌人对象包含模型对象、AI控制对象等。
(2)游戏案例:迷宫游戏
- 资源组织结构:文件夹包含地图纹理文件夹、角色模型文件夹、音效文件夹等。
- 游戏对象树:迷宫对象包含地图对象、墙对象、门对象等,角色对象包含模型对象、控制器对象等。
(3)游戏案例:射击游戏
- 资源组织结构:文件夹包含枪械模型文件夹、子弹纹理文件夹、音效文件夹等。
- 游戏对象树:玩家对象包含摄像机对象、武器对象等,敌人对象包含模型对象、AI控制对象等。
3、编写一个代码,使用 debug 语句来验证 MonoBehaviour 基本行为或事件触发的条件
- 基本行为包括 Awake() Start() Update() FixedUpdate() LateUpdate()
- 常用事件包括 OnGUI() OnDisable() OnEnable()
using UnityEngine;
public class TestScript : MonoBehaviour
{
private void Awake()
{
Debug.Log("Awake");
}
private void Start()
{
Debug.Log("Start");
}
private void Update()
{
Debug.Log("Update");
}
private void FixedUpdate()
{
Debug.Log("FixedUpdate");
}
private void LateUpdate()
{
Debug.Log("LateUpdate");
}
private void OnGUI()
{
Debug.Log("OnGUI");
}
private void OnDisable()
{
Debug.Log("OnDisable");
}
private void OnEnable()
{
Debug.Log("OnEnable");
}
}
4、查找脚本手册,了解 GameObject,Transform,Component 对象
(1)分别翻译官方对三个对象的描述(Description)
GameObject(游戏对象)的描述:游戏对象是所有Unity场景中的实体的基类。它们可以表示角色、道具、环境等。游戏对象具有位置、旋转和缩放,可以添加组件以定义其行为和功能。
Transform(变换)的描述:Transform组件用于存储和操作游戏对象的位置、旋转和缩放。它是游戏对象的必备组件,通过操作Transform的属性和方法,可以实现游戏对象的移动、旋转和缩放等变换操作。
Component(组件)的描述:Component是Unity中所有附加到游戏对象的功能模块的基类。
(2)描述下图中 table 对象(实体)的属性、table 的 Transform 的属性、 table 的部件
- 本题目要求是把可视化图形编程界面与 Unity API 对应起来,当你在 Inspector 面板上每一个内容,应该知道对应 API。
- 例如:table 的对象是 GameObject,第一个选择框是 activeSelf 属性。
table 对象的属性:
- 名称(Name):table
- 标签(Tag):可根据需要进行设置,用于标识和分类游戏对象。
- 活跃状态(Active):指示游戏对象是否处于活跃状态。如果勾选,则对象处于活跃状态;如果取消勾选,则对象处于非活跃状态。
- 静态(Static):指示游戏对象是否是静态的。如果勾选,则对象被认为是静态的,可以进行一些性能优化。
- 层级(Layer):指示游戏对象所在的渲染层级。
table 的 Transform 的属性:
- 位置(Position):指示了游戏对象在世界空间中的位置坐标。
- 旋转(Rotation):指示了游戏对象的旋转角度和方向。
- 缩放(Scale):指示了游戏对象在各个轴向上的缩放比例。
table 的部件(Components):
- Mesh Renderer:用于渲染游戏对象的网格模型,决定了对象的外观。
- Box Collider:提供了一个简单的盒子形状的碰撞器,用于处理游戏对象之间的碰撞检测。
- Rigidbody:给游戏对象添加物理属性,使其能够受到物理引擎的模拟和影响。
(3)用 UML 图描述三者的关系(请使用 UMLet 14.1.1 stand-alone版本出图)
5、资源预设(Prefabs)与 对象克隆 (clone)
(1)预设(Prefabs)有什么好处?
预设(Prefabs)的好处:
- 代码复用:可以在多个场景或不同的游戏对象中重复使用预设,减少代码的编写和维护工作。
- 简化工作流程:通过修改预设,可以同时更新所有使用该预设的游戏对象,提高生产效率。
- 灵活性:预设可以在运行时进行实例化,并动态调整属性和组件,实现动态创建和修改游戏对象的能力。
(2)预设与对象克隆 (clone or copy or Instantiate of Unity Object) 关系?
预设(Prefabs)是一种在Unity中用于创建和管理游戏对象的模板。它允许我们创建一个游戏对象的预定义实例,并在需要时重复使用。预设包含游戏对象的所有组件、属性和初始状态,可以在场景中多次实例化,以便快速创建相同类型的游戏对象。
对象克隆(Clone)是指通过复制一个已有的游戏对象来创建一个新的游戏对象。克隆的游戏对象与原始对象相似,但是它们是独立的实例,可以在游戏中独立进行操作和修改。
预设与对象克隆的关系:
对象克隆可以通过实例化预设来创建游戏对象的副本。预设作为模板定义了游戏对象的初始状态和属性,通过克隆预设可以快速创建相似的游戏对象。克隆的游戏对象是预设的实例,它们可以独立于预设进行修改和操作。
(3)制作 table 预制,写一段代码将 table 预制资源实例化成游戏对象
public class SpawnTable : MonoBehaviour
{
public GameObject tablePrefab;
private void Start()
{
// 实例化table预制资源
GameObject tableObject = Instantiate(tablePrefab);
// 设置实例化对象的位置
tableObject.transform.position = new Vector3(0f, 0f, 0f);
// 设置实例化对象的旋转
tableObject.transform.rotation = Quaternion.identity;
// 设置实例化对象的缩放
tableObject.transform.localScale = new Vector3(1f, 1f, 1f);
}
}
二、 编程实践,小游戏
在unity上完成一个简单的小游戏的制作步骤如下:
1、首先需要先安装unity
2、在进入unity hub后新建一个项目,选择3D项目
3、进入新建的项目,右键下方的Asset中的场景元素,点击Create——C# Script,新建一个C#脚本。
在脚本中编写游戏的程序,我编写了一个简易的四子棋游戏,游戏的规则如下:
- (1)决定先手玩家。每名玩家选一种颜色:白色或者黑色。
- (2)在每个回合,玩家分别从棋盘顶端落下一子。
- (3)游戏轮流进行,直到一方令同色四子在横、竖或斜方向连成一条直线。
我在代码中使用 IMGUI 构建 UI,并且使游戏使用MVC模式,最终代码如下:
-
using System.Collections; using System.Collections.Generic; using UnityEngine; public class NewChessGame : MonoBehaviour {//四子棋 // Entities and their states / Model private static int player; private static int count; private int winner; private int[,] chessBoard = new int[7, 12]; public Texture2D black;//黑棋 public Texture2D white;//白棋 public Texture2D none;//无棋 // System Handlers void Start () { Init(); } // View to render entities / models // Here! you cannot modify model directly, use components/controls to do it void OnGUI() { GUI.Box(new Rect(100, 100, 600, 350), ""); if (GUI.Button(new Rect(250, 40, 300, 50), "Restart")) Init(); if (!GameOver()) { for (int i = 0; i < 7; i++) { for (int j = 0; j < 12; j++) { if (chessBoard[i, j] == 0 && GUI.Button(new Rect(100 + j * 50, 100 + i * 50, 50, 50), none)) { int y = GetTopy(j); if(y!=-1){ PutChess(y,j); } // else{//无法放置 // GUI.Box(new Rect(260, 50, 200, 200), "\n\n\n\n\nThere is no space to place!"); // } } else if (chessBoard[i, j] == 1) GUI.Button(new Rect(100 + j * 50, 100 + i * 50, 50, 50), black); else if (chessBoard[i, j] == 2) GUI.Button(new Rect(100 + j * 50, 100 + i * 50, 50, 50), white); } } } else { if (winner != 0) GUI.Box(new Rect(250, 200, 350, 210), "\n\n\n\n\nCongratulations!\n Player "+winner+" has won."); else GUI.Box(new Rect(250, 200, 350, 210), "\n\n\n\n\n\nThis is a draw!"); } } // Components /controls void Init() { player = 1; winner = 0; count = 0; for(int i = 0; i < 7; i++) for(int j = 0; j < 12; j++) chessBoard[i, j] = 0; } int GetTopy(int j){//获得此时点击的列可放棋子顶部坐标 for(int i=6;i>=0;i--){ if(chessBoard[i,j]==0) return i; } return -1;//无可放棋子 } void PutChess(int i,int j) { chessBoard[i, j] = player; player = 3 - player; //玩家在1号和2号之间切换 count++; } bool GameOver() { for(int i = 0; i < 7; i++) { for(int j = 0; j<12;j++){ if(chessBoard[i,j]!=0){ //获胜条件 if(j+3<12)//未超界 { if(chessBoard[i, j] == chessBoard[i, j+1] && chessBoard[i, j] == chessBoard[i, j+2] && chessBoard[i, j] == chessBoard[i, j+3]) winner = chessBoard[i, j]; } if(i+3<7) { if(chessBoard[i, j] == chessBoard[i+1, j] && chessBoard[i, j] == chessBoard[i+2, j] && chessBoard[i, j] == chessBoard[i+3, j]) winner = chessBoard[i, j]; } if(j+3<12&&i+3<7) { if(chessBoard[i, j] == chessBoard[i+1, j+1] && chessBoard[i, j] == chessBoard[i+2, j+2] && chessBoard[i, j] == chessBoard[i+3, j+3]) winner = chessBoard[i, j]; } if(j+3<12&&i-3>=0) { if(chessBoard[i, j] == chessBoard[i-1, j+1] && chessBoard[i, j] == chessBoard[i-2, j+2] && chessBoard[i, j] == chessBoard[i-3, j+3]) winner = chessBoard[i, j]; } if(j-3>=0&&i-3>=0) { if(chessBoard[i, j] == chessBoard[i-1, j-1] && chessBoard[i, j] == chessBoard[i-2, j-2] && chessBoard[i, j] == chessBoard[i-3, j-3]) winner = chessBoard[i, j]; } if(j-3>=0&&i+3<7) { if(chessBoard[i, j] == chessBoard[i+1, j-1] && chessBoard[i, j] == chessBoard[i+2, j-2] && chessBoard[i, j] == chessBoard[i+3, j-3]) winner = chessBoard[i, j]; } } } } //棋盘被下满 if (count < 84 && winner == 0) return false; return true; } }
将完成的脚本添加进入场景中的素材中就可以运行啦,最终运行效果如下:
-
四子棋小游戏