【游戏开发引擎】实验4:Gauntlet Runner 3D 赛跑游戏


前言

  让我们制作一款游戏!在本实验中,你将制作一款 3D 赛跑游戏,它被适当地命名为Gauntlet Runner。在本实验中,首先将开始设计游戏。接着,将重点关注构建游戏世界。一旦完成,将构建游戏实体和控制。在本实验最后,将尝试玩游戏,看看可以对哪些地方进行改进。


一、实验目的

  1. 怎样设计 Gauntlet Runner 游戏;
  2. 怎样构建 Gauntlet Runner 游戏世界;
  3. 怎样构建 Gauntlet Runner 游戏实体;
  4. 怎样构建 Gauntlet Runner 游戏控制;
  5. 怎样进一步改进 Gauntlet Runner 游戏。

二、实验环境

  1. 操作系统:WINDOWS 10
  2. 开发工具:UNITY
  3. 实验设备:PC

三、实验内容

  制作一款3D赛跑游戏,它被适当地命名为Gauntlet Runner。在游戏中,你将扮演一个机器人,跑步通过一条赛道式隧道,并尝试充电,以延长游戏时间。你需要避开一些会降低速度的障碍物。当你用完时间后,游戏就结束了。


用于Gauntlet Runner游戏的规则如下。
  玩家可以左右移动和跳跃。它们以固定的速度奔跑,并且不能以任何其他的方式移动。
  如果玩家撞上某个障碍物,将把它们的速度降低50%,并持续1 秒钟的时间。
  如果玩家进行了充电,则将把它们的游戏时间延长1.5 秒钟。
  玩家受到屏幕边缘限制。输掉游戏的条件是时间用完了,没有获胜条件。
  可根据需要增加其他内容。

1.游戏世界

场景

(1)创建一个名为 Gauntlet Runner 的新项目,然后创建一个名为 Scenes 的新文件夹,并在该文件夹中把场景另存为 Main。
(2)向场景中添加一个定向灯光。
在这里插入图片描述
(3)把 Main Camera 定位于(0, 3,−10.7)处,并把旋转角度设置为(33, 0, 0)。然后保存场景。
在这里插入图片描述

地面

(1)向场景中添加一个立方体,把它命名为 Ground,然后把它定位于(0, 0, 15.5)处,并把缩放比例设置为(10, .5, 50)。向场景中添加另一个名为 Wall 的立方体,然后把它定位于(−5.5, .7, 15.5)处,并把缩放比例设置为(1, 1, 50)。复制墙壁部分,并把新的墙壁项目定位于(5.5, .7, 15.5)处。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(2)创建两个新文件夹:Textures 和 Materials。把Checker.tga文件拖到 Textures 文件夹中。在 Materials 文件夹中,创建一种新材质,并把它命名为 GroundMaterial。
在这里插入图片描述
在这里插入图片描述
(3)把 GroundMaterial 的纹理设置为你刚才导入的 Checker 文件。修改材质的 Main Color属性,给它提供一种微红色。然后把该材质应用于地面和墙壁。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

滚动地面

(1)创建一个名为 Scripts 的新文件夹,然后创建一个名为 GroundScript 的新脚本,并把该脚本同时附加到地面和墙壁上。
(2)运行场景,并且注意到赛道滚动。
在这里插入图片描述
GroundScript代码如下:

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

public class GroundScript : MonoBehaviour
{
    
    
    float speed = .5f;
    void Update()
    {
    
    
        float offset = Time.time * speed;
        GetComponent<Renderer>().material.mainTextureOffset = new Vector2(0, -offset);
    }
    public void SlowDown()
    {
    
    
        speed = speed / 2;
    }
    public void SpeedUp()
    {
    
    
        speed = speed * 2;
    }
}

2.实体

充电装置

(1)向场景中添加一个球体,并把它定位于(0, 1, 42)处。给该球体添加一个刚体,并取消选中 Use Gravity。
在这里插入图片描述
(2)创建一种名为 PowerupMaterial 的新材质,并把它设置为黄色。然后把该材质应用于球体。
在这里插入图片描述
在这里插入图片描述
(3)给球体添加一个点光源(单击 Component > Rendering > Light 命令),并把它设置为黄色。然后给球体添加一个粒子系统(单击 Component > Effects > Particle System 命令),然后把粒子的起始颜色设置为黄色,并把起始寿命设置为 2.5。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
(4)创建一个名为 Prefabs 的新文件夹,在该文件夹中创建一个新的预设,并把它命名为Powerup。从 Hierarchy 视图中单击球体,并把它拖到预设上。然后从场景中删除球体。
在这里插入图片描述

障碍物

(1)向场景中添加一个立方体,然后把它定位于(0, .4, 42)处,并把缩放比例设置为(1, .2, 1)。给立方体添加一个刚体,并取消选中 Use Gravity。
在这里插入图片描述
在这里插入图片描述
(2)创建一种名为 ObstacleMaterial 的新材质,所该材质的颜色设置为黑色,并把它应用于立方体。
在这里插入图片描述
在这里插入图片描述
(3)创建一个名为 Obstacle 的新预设,从 Hierarchy 视图中把立方体拖到预设上,然后删除该立方体。

在这里插入图片描述

触发器区域

(1)向场景中添加一个立方体,把该立方体命名为 TriggerZone,然后把它定位于(0, 1, −20)处,并把缩放比例设置为(10, 1, 1)。
在这里插入图片描述
(2)在触发器区域的 Box Collider 组件上,选中 Is Trigger 属性。
在这里插入图片描述

3.玩家

(1)在用于本实验配套资源中找到名为 Robot Kyle 的文件夹,把该文件夹拖到 Unity 中的 Project 视图中,以导入它。
在这里插入图片描述
(2)在 Robot Kyle 文件夹下的 Model 文件夹中找到并选择 Robot Kyle.fbx 文件。然后在 Inspector 视图中,选择 Animations 选项卡,并取消选择 Import Animation。然后单击 Apply按钮。
在这里插入图片描述
(3)在 Rig 选项卡下,把动画类型改为 Humanoid。然后单击 Apply 按钮。现在应该会在 Configure 按钮旁边看到一个选中标记。如果没有看到它,将需要单击 Configure 按钮,并且配置绑定。
在这里插入图片描述

使动画准备好放在动画器中

(1)在本实验配套资源中找到 Animations 文件夹,并把该文件夹拖到 Unity 中的 Project视图中,以导入它。
在这里插入图片描述
(2)在新导入的 Animations 文件夹中,找到 Jump.fbx 文件并选择它。在 Inspector 视图中,单击 Rig 选项卡,并把动画类型改为 Humanoid。然后单击 Apply 按钮。
在这里插入图片描述
(3)在 Animations 选项卡下,修改跳跃动画的属性。注意,RootTransform Rotation属性下面的Offset 属性可能需要不同于图中所示的属性,重要的是AverageVelocity 属性对于 x 轴具有值 0。然后单击 Apply 按钮。
在这里插入图片描述
(4)在 Animations 文件夹中选择 Runs.fbx 文件。再次完成第(2)步,为这个模型校正绑定。在 Animations 选项卡下,注意有 3 个剪辑:RunRight、Run 和 RunLeft。选择 Run,确保属性匹配。同样,重要的部分是用于 Average Velocity 属性的 x 轴的值为 0。然后单击 Apply 按钮。
在这里插入图片描述

准备动画器

(1)创建一个名为 Animators 的新文件夹,并在该文件夹中创建一个新的动画器(右键单击并选择 Create > Animator Controller 命令),并把它命名为 PlayerAnimator。
在这里插入图片描述
(2)双击动画器,打开 Animator 视图。在 Animations 文件夹中,通过单击它左边的箭头找到 Runs.fbx 文件。在展开的模型中,找到 Run 动画剪辑,并把它拖到 Animator 视图上。单击新创建的 Run 状态,然后在 Inspector 视图中选中 Foot IK 属性。
在这里插入图片描述
(3)在 Animations 文件夹中找到 Jump.fbx 文件。展开该文件,找到 Jump 动画剪辑,并把该剪辑拖到 Animator 视图上。单击新创建的 Jump 状态,然后在 Inspector 视图中选中 Foot IK属性,并把 Speed 属性改为 1.25。
在这里插入图片描述
(4)在 Animator 视图中单击 Parameters 框中的加号(+),给动画器添加一个新的参数。该参数应该是一个名为 Jumpingr 布尔型参数。
在这里插入图片描述
(5)在动画器中右键单击 Run 状态,并选择 Make Transition。单击 Jump 状态,把它们链接在一起。然后右键单击 Jump 状态,并选择 Make Transition,把它链接回 Run 状态。
在这里插入图片描述
(6)单击白色箭头,从 Run 过渡到 Jump。在 Inspector 视图中,把 Conditions 改为“当 Jumping 是 True 时。
在这里插入图片描述
(7)单击白色箭头,从 Jump 过渡到 Run。确保 Inspector 视图中的属性。
在这里插入图片描述

玩家模型

(1)找到 Robot Kyle.fbx 文件,并把它拖到场景中。然后把机器人定位于(0, .25, −8.5)处。
在这里插入图片描述
(2)给模型添加一个胶囊碰撞器(单击 Component > Physics > Capsule Collider 命令)。然后把碰撞器的 y 值设置为 0.95,并把碰撞器的高度设置为 1.72。
在这里插入图片描述
(3)把 PlayerAnimator 拖到 Animator 组件的 Controller 属性上,还要确保取消选中 Apply Root
Motion 复选框。
在这里插入图片描述

4.控制

触发器区域脚本

创建一个名为TriggerZoneScript的新脚本,并把它附加到触发器区域游戏对象上。
在这里插入图片描述
TriggerZoneScript代码如下:

using UnityEngine;
using System.Collections;

public class TriggerZoneScript : MonoBehaviour {
    
    

	// Use this for initialization
	void Start () {
    
    
	}
	
	// Update is called once per frame
	void Update () {
    
    
	}
	
	void OnTriggerEnter(Collider other)
	{
    
    
		Destroy (other.gameObject);
	}
}

游戏控制脚本

(1)在场景中创建一个空的游戏对象,并把它命名为 GameControl,它将只是脚本的一个占位符。
(2)创建一个名为 GameControlScript的新脚本,并把它附加到你刚才创建的游戏控制对象上。
在这里插入图片描述
GameControlScript代码如下:

using UnityEngine;
using System.Collections;

public class GameControlScript : MonoBehaviour {
    
    
	
	public float objectSpeed = -.3f;
	float minSpeed = -.15f;
	float maxSpeed = -.3f;
	
	public GroundScript ground;
	public GroundScript wall1;
	public GroundScript wall2;
	
	float timeRemaining = 10;
	float timeExtension = 1.5f;
	float totalTimeElapsed = 0;
	
	bool isGameOver = false;
	
	// Use this for initialization
	void Start () {
    
    
	
	}
	
	// Update is called once per frame
	void Update () {
    
    
		
		if(isGameOver)
			return;
		
		
		totalTimeElapsed += Time.deltaTime;
		timeRemaining -= Time.deltaTime;
		if(timeRemaining <= 0)
			isGameOver = true;
	}
	
	public void SlowWorldDown()
	{
    
    
		CancelInvoke("SpeedWorldUp");
		
		objectSpeed = minSpeed;
		ground.SlowDown();
		wall1.SlowDown();
		wall2.SlowDown();
		
		Invoke ("SpeedWorldUp", 1);
	}
	
	void SpeedWorldUp()
	{
    
    
		objectSpeed = maxSpeed;
		ground.SpeedUp();
		wall1.SpeedUp();
		wall2.SpeedUp();
	}
	
	public void PowerupCollected()
	{
    
    
		timeRemaining += timeExtension;
	}
	
	void OnGUI()
	{
    
    
		if(!isGameOver)
		{
    
    
			GUI.Box(new Rect(Screen.width / 2 - 50, Screen.height - 100, 100, 50), "Time Remaining");
			GUI.Label(new Rect(Screen.width / 2 - 10, Screen.height - 80, 20, 40), ((int)timeRemaining).ToString());
		}
		else
		{
    
    
			GUI.Box(new Rect(Screen.width / 2 - 60, Screen.height / 2 - 100, 120, 50), "Game Over");
			GUI.Label(new Rect(Screen.width / 2 - 55, Screen.height / 2 - 80, 90, 40), "Total Time: " + (int)totalTimeElapsed);
		}
	}
}

玩家脚本

创建一个名为PlayerScript 的新脚本,并把它附加到场景中的机器人模型上。
在这里插入图片描述
PlayerScript代码如下:

using UnityEngine;
using System.Collections;

public class PlayerScript : MonoBehaviour {
    
    
	
	public GameControlScript control;
	
	float strafeSpeed = 2;
	Animator anim;
	
	bool jumping = false;
	
	void Start () {
    
    
		anim = GetComponent<Animator>();
	}
	
	void Update () {
    
    
		transform.Translate(Input.GetAxis("Horizontal") * Time.deltaTime * strafeSpeed, 0f, 0f);
		
		if(transform.position.x > 3)
			transform.position = new Vector3(3, transform.position.y, transform.position.z);
		else if(transform.position.x < -3)
			transform.position = new Vector3(-3, transform.position.y, transform.position.z);
		
		if (anim.GetCurrentAnimatorStateInfo(0).IsName("Base Layer.Jump"))
		{
    
    
			anim.SetBool("Jumping", false);
			jumping = true;
		}
		else
		{
    
    
			jumping = false;
			if(Input.GetButtonDown("Jump"))
				anim.SetBool("Jumping", true);
		}
	}
	
	void OnTriggerEnter(Collider other)
	{
    
    
		if(other.gameObject.name == "Powerup(Clone)")
		{
    
    
			control.PowerupCollected();
		}
		else if(other.gameObject.name == "Obstacle(Clone)" &&
			jumping == false)
		{
    
    
			control.SlowWorldDown();
		}
		
		Destroy(other.gameObject);
	}
}

充电装置和障碍物的脚本

创建两个脚本,分别命名为PowerupScript 和 ObstacleScript。把充电装置脚本添加给充电装置预设,其方法是选择预设,并在 Inspector 中单击 Add Component > Scripts > Powerup Script 命令。
在这里插入图片描述
PowerupScript 代码如下:

using UnityEngine;
using System.Collections;

public class PowerupScript : MonoBehaviour {
    
    
	
	public GameControlScript control;
	
	// Use this for initialization
	void Start () {
    
    
	}
	
	// Update is called once per frame
	void Update () {
    
    
		transform.Translate(0, 0, control.objectSpeed);
	}
}

在这里插入图片描述
ObstacleScript代码如下:

using UnityEngine;
using System.Collections;

public class ObstacleScript : MonoBehaviour {
    
    
	
	public GameControlScript control;
	
	
	// Use this for initialization
	void Start () {
    
    
	}
	
	// Update is called once per frame
	void Update () {
    
    
		transform.Translate(0, 0, control.objectSpeed);
	}
}

复活脚本

创建一个名为 SpawnScript 的新脚本,并把它附加到 GameControl 对象上。
在这里插入图片描述
SpawnScript代码如下:

using UnityEngine;
using System.Collections;

public class SpawnScript : MonoBehaviour {
    
    
	
	GameControlScript control;
	
	public GameObject obstacle;
	public GameObject powerup;
	
	float timeElapsed = 0;
	float spawnCycle = .5f;
	bool spawnPowerup = true;
	
	// Use this for initialization
	void Start () {
    
    
		control = GetComponent<GameControlScript>();
	}
	
	// Update is called once per frame
	void Update () {
    
    
		timeElapsed += Time.deltaTime;
		if(timeElapsed > spawnCycle)
		{
    
    			
			GameObject temp;
			if(spawnPowerup)
			{
    
    
				temp = (GameObject)Instantiate(powerup);
				temp.GetComponent<PowerupScript>().control = control;
				Vector3 pos = temp.transform.position;
				temp.transform.position = new Vector3(Random.Range(-3, 4), pos.y, pos.z);
			}
			else
			{
    
    
				temp = (GameObject)Instantiate(obstacle);
				temp.GetComponent<ObstacleScript>().control = control;
				Vector3 pos = temp.transform.position;
				temp.transform.position = new Vector3(Random.Range(-3, 4), pos.y, pos.z);
			}
			
			timeElapsed -= spawnCycle;
			spawnPowerup = !spawnPowerup;
		}
		
	}
}

把游戏的各个部分结合起来

5.游戏测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


总结

  了解粒子系统,必须先了解每一个属性都代表了什么,之后才能根据这些原理来调整出自己满意的效果。
  主面板ParticleSystem:

  • Duration:粒子发射周期,如图的意思就是在发射3.32秒以后进入下一个粒子发射周期。如果没有勾选looping的话,3.32秒之后粒子会停止发射。
  • Looping:粒子按照周期循环发射。
  • Prewarm:预热系统,比如说我有一个空间大小的粒子系统,但是粒子发射速度有限,我想在最开始的时候让粒子充满空间,此时就应该勾选Prewarm。
  • StartDelay:粒子延时发射,勾选后,延长一段时间才开始发射。
  • StartLifeTime:粒子从发生到消失的时间长短。
  • StartSpeed:粒子初始发生时候的速度。
  • 3DStartSize:这个属性是当你需要把粒子在某一个方向上扩大的时候使用。
  • StartSize:粒子初始的大小。
  • 3DStartRotation:需要在一个方向旋转了子的时候可以使用。
  • StartRotation:粒子初始旋转。
  • RandomizeRotation:随机旋转粒子方向,感觉在3D粒子的情况下,尤其是圆形的没什么用。
  • StartColor:粒子初始颜色,可以调整加上渐变色。
  • GravityModifier:重力修正。
  • SimulationSpace:a.Local,此时粒子会跟随父级物体移动。b.World,此时粒子不会跟随父级移动。c.Custom,粒子会跟着指定的物体移动。
  • SimulationSpeed:根据Update模拟的速度。
  • DeltaTime:一版的DeltaTime都是1,如果需要用到Sacled是在游戏需要暂停的时候,根据TimeManager来定。如果选择UnScale的话,就会忽略时间的影响。
  • ScalingMode:Local:粒子系统的缩放和自己Transform的一样会忽略父级的缩放。Hierarchy:粒子缩放跟随父级。Shape:将粒子系统跟随初始位置,但是不会影响粒子系统的大小。
  • EmitterVelocity:
  • MaxParticles:粒子系统可以同时存在的最大粒子数量。如果粒子书数量超过最大值粒子系统会销毁一部分粒子。
  • AutonRandomSeed:随机种子,如果勾选会生成完全不同不重复的粒子效果,如果勾选即为可重复。

  Animation Controller(动画控制器)是Animator的重要组成部分,是模型动画的驱动,动画控制器是通过动画状态机来实现不同动画剪辑的切换。
  这里有两个关键词:
  1.状态(动画剪辑),动画剪辑就是每个动画模型所可以执行的动画剪辑(Animation),比如休闲(Ideal)、走(Walk)、跑(Run)等。状态以圆角矩形表示,黄色表示默认的状态,即模型的默认动画状态。在实际操作中只要将动画剪辑拖拽到Animation Controller中就可以成为一个状态。
每个状态就是一段动画,可以通过动画剪辑中的speed属性来控制动画播放速度,甚至进行倒序播放。如下图中的Walk-Sheathedback和Walk-Sheathed其实为一个动画剪辑,但是Walk-Sheathedback将速度设定为-1,则其将由Walk-Sheathed的最后一帧往第一帧播放。
  2.过渡(Transition),过渡指不同动画之间的切换条件,在动画状态机中以方向箭头表示。状态之间的过渡需要通过状态参数来进行控制,即在Unity中通过脚本控制状态参数之间的变化来控制状态之间的变化。比如在我们可以设定一个Float参数Speed来控制Idle、Walk和Run之间的切换。Idle与Walk之间:Speed>0,Walk;Speed<0.01,Idle。Walk与Run之间,Speed>0.5,Run;Speed<0.5,Walk。通过这个设定我们只要用如下脚本就可以实现模型在Idle、Walk和Run之间切换。

  三种实现运动游戏场景中感觉是往前(后、左、右、 上、下等)移动的方法:
  1.直接控制修改游戏主角和其它游戏对象 Transform 组件各属性值的方式,使对象的位置在场景中真实改变,同时通过移动时间间隔和移动步长实现速度快慢的体现。
  2.游戏主角在场景中不动,通过控制其它(特别是表现背景等的)游戏对象相对游戏主角移动方向相反的方向移动,从而看起来感觉游戏主角在移动。
  3.游戏主角和代表场景中背景的对象都不动,而只是让代表背景的对象的纹理坐标发生有规律的偏移,使纹理相对游戏主角移动方向相反的方向移动,从而看起来感觉游戏主角在移动。

  在本实验中,制作了 Gauntlet Runner 游戏。首先布置了游戏的设计元素。接着构建了赛道,并且使用纹理技巧使之滚动。然后为游戏构建了多个实体。接着构建了多个控制和脚本。最后测试了游戏并且记录了一些反馈。

猜你喜欢

转载自blog.csdn.net/qq_45614178/article/details/122375090