期末大实验Unity游戏制作
一. 摘要
借鉴于经典小游戏《反转重力战士》,主角具有反转自身重力的能力。本文利用游戏引擎Unity实现了主要功能,包括:1.角色逻辑实现 2.关卡设计 3.UI。下面将一一介绍。
二. 功能模块
1. 角色逻辑实现
1.1 角色主体
使用了unity默认的正方形sprite作为角色主体
1.2 组件概述
角色主体附加了多个组件,包括:默认的Transform、Sprite Render以及附加的2个Box Collider 2D、1个Rigidbody 2D和自定义的C#脚本Player Control。
1.3 组件详解
1.3.1 Rigidbody 2D组件
该组件中文名为2D刚体组件,本游戏中将会用到两个该组件中的功能——Body Type和Constraints。
Body Type功能有三个选项Dynamic、Kinematic和Static,游戏中只用到第一和第三两个功能。Dynamic表示该对象动态性地与所有物体交互,适用于游戏主体,而Static表示该对象完全不动且只与Dynamic物体交互,将会用作地面的制作。
Constrains中包括三个选项框,分别冻结x、y、z三个轴。这里选择冻结z轴避免主角移动时翻滚。
1.3.2 Box Collider 2D
Box Collider 2D是一个盒型的碰撞体组件,控制主角与环境的碰撞。游戏中使用的功能主要是IsTrigger。该功能决定碰撞器是否作为触发器使用。碰撞器用于与环境的物理交互,触发器用于特殊的检测(如捡拾金币)。使用两个Box Collider 2D的原因在于各使用一个盒型碰撞器和一个触发器。
此处触发器的大小为(0.8 , 1.01)是避免因多次触发产生的BUG。
1.3.3 C#脚本-PlayerControl
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControl : MonoBehaviour
{
private Rigidbody2D rb;
private Collider2D coll;
private Transform trans;
public GameObject camera;
private bool jumpAction = false;
private bool isReverse = false;
public static int gameStatus = 0;
public float speedStep = 100;
public float speed;
public float jumpForce = 100;
public int jumpCount = 1;
public static int grades = 0;
static public float interval = 0;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
coll = GetComponent<Collider2D>();
trans = GetComponent<Transform>();
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Jump")&&jumpCount>=1)
{
jumpAction = true;
}
interval -= Time.deltaTime;
if (Input.GetButtonDown("Reverse")&&interval <= 0)
{
isReverse = true;
interval = 1.5f;
}
GameFail();
}
void FixedUpdate()
{
MoveMent();
Jump();
Reverse();
}
void MoveMent()
{
if (Input.GetAxis("Horizontal") != 0)
{
speed = Input.GetAxis("Horizontal") * speedStep * Time.deltaTime;
rb.velocity = new Vector2(speed, rb.velocity.y);
}
else
{
rb.velocity = new Vector2(rb.velocity.x * 14/15, rb.velocity.y);
}
}
void Jump()
{
if (jumpAction && jumpCount >= 1)
{
jumpCount--;
jumpAction = false;
if (rb.gravityScale >= 0)
rb.velocity = new Vector2(rb.velocity.x, jumpForce * Time.deltaTime);
else
rb.velocity = new Vector2(rb.velocity.x, -1 * jumpForce * Time.deltaTime);
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Ground")
{
jumpCount = 1;
rb.velocity = new Vector2(rb.velocity.x, 0);
}
if (collision.gameObject.tag == "Gift")
{
Destroy(collision.gameObject);
grades += 10;
}
if(collision.gameObject.tag=="Gate")
{
gameStatus = 1;
}
}
void Reverse()
{
if (isReverse)
{
rb.velocity = new Vector2(rb.velocity.x, 0);
rb.gravityScale *= -1;
isReverse = false;
}
}
void GameFail()
{
if (trans.position.x < camera.transform.position.x - 25 || trans.position.y < -7 ||trans.position.y > 20 )
{
gameStatus = -1;
}
}
}
顾名思义,Unity使用C#作为脚本语言实现。每一个Unity中的C#脚本都相当与一个MonoBehaviour的派生类,从而可以调用许多便利的内置成员函数。它包括两个基本的函数 Start()、Update()。前者负责开始阶段执行一次,后者以固定的时间不断执行。还有一个FixedUpdate(),它与Update()很像,但是以固定的帧率执行,适用于不同的计算机。还有一个特殊的函数 private void OnTriggerEnter2D(Collider2D collision) ,该函数会使用上文的触发器并执行内部代码。
Start()函数中使用了 GetComponent()模板函数,可以在列表窗口中寻找相对应的组件并赋值给相应定义的变量,使得这些组件可被脚本编程。
Update()函数中有三个关于游戏状态控制的代码块。第一个if中的Input.GetButtonDown(“Jump”)&&jumpCount>=1表示当按下键“Jump”(默认为“W”)且jumpCount>=1时将jumpAction变为true。第二个if前使用了一个计时器 interval -= Time.deltaTime;作为代码块内容的CD,if中的判断条件Input.GetButtonDown(“Reverse”)&&interval <= 0表示按下“Reverse”键(默认为“SPACE”)并且计时器小于0时将isReverse设置为true并重置计时器。第三个函数是一个实时判断游戏主角状态的函数,如果玩家位置超出摄像机边界则将gameSta-tus设置为-1,视为游戏失败。
FixedUpdate()里面有三个函数,按固定帧率执行MoveMent()、Jump()、Reverse()。
MoveMent()函数使用了if (Input.GetAxis(“Horizontal”) != 0)分支控制流程。Input.GetAxis(“Horizontal”)接受“A”和“D”的键入并映射到一个范围为-1到1的值(不键入即为0)。若玩家按下“A”或“D”,则执行第一个代码块中的代码,为speed赋值,并将其作为主角沿x轴的速度。乘以TIme.deltaTime保证速度为帧间隔的倍数,以防频闪的发生。否则执行第二个代码块中的内容,每帧将x方向上的速度变为14/15,达到滑步的效果。
Jump()首先检查jumpAction的状态,如为true则执行代码。进入Jump()后首先重置了jumpAction状态并使jumpCount减一,再根据角色的Gravityscale判定向上跳还是向下跳。
OnTriggerEnter2D(Collider2D collision)函数在其他碰撞体进入触发器时触发,然后通过collision.gameObject.tag判断是碰撞物体的标签(tag),如果是“Ground”则表示触地,应当使jumpAction为false表示结束跳跃,同时使jumpCount加一;如果是“Gift”则表示捡拾金币,应该使分数grade增加;如果是“Gate”则表示玩家到达出口,将gameStatus设置为1表示游戏胜利。
2. 关卡设计
2.1 StartScene
开始场景的只设计了UI,鼠标点击按钮即可跳转关卡,具体实现在UI中详述。
2.2 Level1
2.2.1 UI
图中UI包括左上角的GRADES:,捡拾金币可以显示加分;右下角的Reverse指示器,当按下“SPACE”将反转并变成红色。具体实现同样在UI中详述。
2.2.2 相机
游戏的相机会以一定速度向右平移,促使玩家的操作。这里使用C#脚本实现。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Camera : MonoBehaviour
{
public int defalutSpeed = 500;
private Rigidbody2D rb;
// Start is called before the first frame update
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
// Update is called once per frame
void Update()
{
}
private void FixedUpdate()
{
rb.velocity = new Vector2( defalutSpeed * Time.deltaTime ,rb.velocity.y);
}
}
这里使用GetComponent()获取刚体组件,并在FixedUpdate()中实时赋给相机一个恒定的x轴速度,使用Time.deltaTime保证画面连续性。
2.2.3 地面Ground
图中的黑色部分为Ground,它使用了Static的Rigidbody 2D组件,并附带一个Box Collider 2D组件,标签被设置为ground,以便于主角交互。
2.2.4 金币Gift
黄色的圆就是金币,它只有一个作为触发器的Circle Collider 2D,但是标签被设置为Gift,以便于主角交互。
2.2.5 出口Gate
本体为白色的矩形Sprite,附加了一个作为触发器的Box Collider 2D,同时标签被设置为Gate,以便主角交互。
2.3 EndScene
与StartScene相同,该场景只有UI,详解于下。
2.4 UI
2.4.1 画布
Unity中要使用UI画布是必不可少的,它相当于一个映射到玩家屏幕上的标准场景。它可以附加多个与UI相关的组件,如文本、按钮、C#脚本。
2.4.2 文本
文本组件可以在画布上显示输入的文本,并根据设置详细对字体、文本效果等个性化定制。
2.4.3 按钮
按钮组件可以检测鼠标的点击,其内置了一个text脚本可以用C#脚本调用。
2.4.4 C#脚本-按钮
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ButtonStartGame : MonoBehaviour
{
public Button btn;
public Text txt;
void Start()
{
btn = GetComponent<Button>();
txt = btn.transform.Find("Text").GetComponent<Text>();
txt.fontSize += 50;
if (PlayerControl.gameStatus == 0)
{
txt.text = "START GAME";
}
else
txt.text = "RETRUN MENU";
}
public void OnMouseUpAsButton()
{
if (PlayerControl.gameStatus == 0)
{
PlayerControl.grades = 0;
Application.LoadLevel(1);
}
else
{
PlayerControl.gameStatus = 0;
Application.LoadLevel(0);
}
}
}
该脚本通过检测PlayerControl内的静态变量gameStatus的值来判断当前所处的场景并输出相应的Text文本。
它还使用了一个OnMouseUpAsButton()函数,在鼠标点击时调用并使用Application.LoadLevel()函数切换到其他关卡。
2.4.5 Level1
该UI本质是一个image,通过附加的C#脚本改变箭头方向与颜色。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GameUI : MonoBehaviour
{
public RectTransform rTrans;
private Image ima;
private bool isReverse = false;
// Start is called before the first frame update
void Start()
{
rTrans = GetComponent<RectTransform>();
ima = GetComponent<Image>();
}
// Update is called once per frame
void Update()
{
if (Input.GetButtonDown("Reverse")&&PlayerControl.interval <= 0)
isReverse = true;
}
private void FixedUpdate()
{
Reverse();
ColorJudge();
}
void Reverse()
{
if (isReverse)
{
rTrans.localScale = new Vector3(1, -1 * rTrans.localScale.y, 1);
isReverse = false;
}
}
void ColorJudge()
{
if (rTrans.localScale.y > 0)
ima.color = new Color(255, 255, 255);
else
ima.color = new Color(255, 0, 0);
}
}
该脚本也定义了Reverse()函数,通过相同的检测调用,并反转当前的localScale改变箭头方向。
该脚本还定义了ColorJudge()函数,通过对localScale.y的检测改变image的颜色。
三. 总结
1. 游戏示例
1.1 游戏开始
1.2 游戏界面
1.3 游戏结束
2. 总结
本次作业完成了一个小游戏制作的基本流程,但未对核心机制Reverse做出更有趣的应用。在脚本部分并不复杂,主要是对游戏引擎Unity操作的熟悉。
1.3 游戏结束
2. 总结
本次作业完成了一个小游戏制作的基本流程,但未对核心机制Reverse做出更有趣的应用。在脚本部分并不复杂,主要是对游戏引擎Unity操作的熟悉。