让我们制作一款游戏!在本实验中,将制作一款名称为 Captain Blaster 的 2D 滚动射击游戏。你首先将设计游戏的多个元素。接着,将开始构建滚动背景。一旦建立了运动的理念,就可以开始构建多个游戏实体。在完成了实体之后,将构造控制并使项目游戏化。在本实验最后,将分析游戏,并确定一些可以改进的地方。
制作一款名称为Captain Blaster的2D滚动射击游戏。你首先将设计游戏的多个元素。接着,将开始构建滚动背景。一旦建立了运动的理念,就可以开始构建多个游戏实体。在完成了实体之后,将构造控制并使项目游戏化。在本实验最后,将分析游戏,并确定一些可以改进的地方。
(1)在名为 Captain Blaster 的文件夹中创建一个新项目,并向场景中添加一个定向灯光。
(2)创建一个 Scenes 文件夹,并把场景另存为 Main。
(3)在 Game 视图中,把屏幕高宽比改为 5:4
(1)把摄像机定位于(0, 0, −10)处,并且不进行旋转。
(2)把 Projection 属性改为 Orthographic。
(3)把 Size 属性设置为 6,查看摄像机的属性列表。
(1)向场景中添加一个立方体,把它重命名为 Background,并把它置于(0, 0, 0)处。然后立方体缩放为(15, 15, .1)。
(2)在 Project 视图中创建一个名为 Textures 的新文件夹。在用于第 15 章(Hour 15)的本书配套资源中找到 Star_Sky.png 文件,并把它拖到新的 Textures 文件夹中。从 Project 视图中,把 Star_Sky 纹理拖到背景上。
(3)在 Project 视图中创建一个名为 Scripts 的新文件夹。然后在该文件夹中创建一个名为
BackgroundScript 的新脚本,并把它拖到背景立方体上。
BackgroundScript代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BackgroundScript : MonoBehaviour
{
public float speed = -2;
// Use this for initialization
void Start()
{
}
// Update is called once per frame
void Update()
{
transform.Translate(0f, speed * Time.deltaTime, 0f);
if (transform.position.y <= -15)
{
transform.Translate(0f, 30f, 0f);
}
}
}
(1)创建一个新文件夹,并把它命名为 Meshes。将Space Shooter的文件夹拖到新创建的Meshes文件夹中(以导入它)。
(2)在 Meshes 文件夹下,现在应该会有一个 Space Shooter 文件夹。找到其中的Space_Shooter.fbx 文件,并在编辑器中把比例因子改为 0.09。一定要单击Inspector 视图底部的 Apply 按钮。
(3)从 Project 视图中单击 Space_Shooter.fbx,并把它拖到 Scene 视图中。注意它面朝错误的方向。把它定位在(0, −4, −5)处,并把旋转角度设置为(270, 0, 0)。
(4)在 Space Shooter 文件夹下找到 Textures 文件夹,然后单击 1K_BodyTXTR.jpg 文件,并将其拖到 Scene 视图中的宇宙飞船模型上。
(5)给宇宙飞船添加一个胶囊碰撞器。选中 Is Trigger 属性,然后把半径设置为 0.62,把高度设置为 1.71,并把方向设置为 Z-Axis
(1)将Meteor1 文件夹拖到创建的 Meshes 文件夹中。
(2)在新的 Meteor1 文件夹中找到 Meteor1.fbx 文件,然后在 Inspector 视图中把比例因子改为 0.5。一定要单击 Inspector 视图底部的 Apply 按钮。
(3)把 Meteor1.fbx 文件拖到 Scene 视图中。把它置于(0, 0, −5)处,然后把旋转角度设置为(0, 0, 0),并把缩放比例设置为(1, 1, 1)(网格在导入时已经应用了某种旋转和缩放效果)。
(4)在 Textures 文件夹中,找到 Meteor1_TXTR.png 文件,并把它拖到场景中的流星上。
(5)给流星添加一个刚体,并且取消选中 Use Gravity 属性。给流星也添加一个胶囊碰撞器。
(6)创建一个名为 Prefabs 的新文件夹,并在该文件夹中创建一个名为 Meteor 的新预设。从 Hierarchy 视图中单击 Meteor1 对象,并将其拖到新创建的预设上。然后从场景中删除Meteor1 对象。
(1)向场景中添加一个胶囊,把它定位于(0, 0, 0)处,并把缩放比例设置为(.1, .1, .1)。给胶囊添加一个刚体,并取消选中 Use Gravity 属性。
(2)如果还没有 Materials 文件夹,就创建该文件夹,并在其中创建一种名为 BulletMaterial的新材质。给该材质提供明亮的绿色,并把它应用于子弹。
(3)创建一个名为 Bullet 的新预设,然后单击胶囊并把它拖到子弹预设上。现在从场景中删除胶囊。
(1)向场景中添加一个立方体,并把它命名为 Trigger。把该立方体定位在(0, −9, −5)处,并把缩放比例设置为(15, 1, 1)。
(2)在 Inspector 视图中,确保选中 Box Collider 组件的 Is Trigger 属性。
(3)复制触发器,并把新的触发器放在(0, 9, −5)处。
创建一个空的游戏对象,并把它命名为 GameControl。创建一个名为 GameControlScript 的新脚本,并把它附加到游戏控制对象上。
GameControlScript代码如下:
using UnityEngine;
using System.Collections;
public class GameControlScript : MonoBehaviour
{
//is the game still going?
bool isRunning = true;
int playerScore = 0;
void Start() { }
void Update() { }
public void AddScore()
{
playerScore++;
}
public void PlayerDied()
{
isRunning = false;
}
void OnGUI()
{
if (isRunning == true)
{
GUI.Label(new Rect(5, 5, 100, 30), "Player Score: " +
playerScore);
}
else
{
GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 -
50, 200, 100), "Game Over. Your score was: " + playerScore);
}
}
}
创建一个新脚本,并把它命名为 MeteorScript。在 Prefabs 文件夹中,选择 Meteor 预设。在 Inspector 视图中,找到AddComponent 按钮。然后单击Add Component > Scripts > Meteor Script 命令。
MeteorScript代码如下:
using UnityEngine;
using System.Collections;
public class MeteorScript : MonoBehaviour
{
float speed = -5f;
//random rotation
float rotation;
void Start()
{
rotation = Random.Range(-40, 40);
}
void Update()
{
transform.Translate(0f, speed * Time.deltaTime, 0f);
transform.Rotate(0f, rotation * Time.deltaTime, 0f);
}
}
创建一个新的空游戏对象,把该对象重命名为 MeteorSpawn,并将其置于(0, 7, −5)处。然后创建一个名为 MeteorSpawnScript 的新脚本,并把它放在流星再生对象上。在 Unity 编辑器中,从 Project 视图中单击 Meteor 预设,并将其拖到流星再生对象的Meteor Spawn Script 组件的 Meteor 属性上。
MeteorSpawnScript代码如下:
using UnityEngine;
using System.Collections;
public class MeteorSpawnScript : MonoBehaviour {
//meteor spawning timers
float spawnThreshold = 100;
float spawnDecrement = .1f;
//meteor prefab
public GameObject meteor;
void Start () {}
void Update () {
if(Random.Range(0, spawnThreshold) <= 1)
{
//create a meteor at a random x position
Vector3 pos = transform.position;
Instantiate(meteor, new Vector3(pos.x + Random.Range(-6, 6), pos.y, pos.z), Quaternion.identity);
spawnThreshold -= spawnDecrement;
}
if(spawnThreshold < 2)
{
spawnThreshold = 2;
}
}
}
创建一个名为 TriggerScript 的新脚本,并把它同时附加到你在前面创建的上下两个触发器对象上。
TriggerScript代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TriggerScript : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
void OnTriggerEnter(Collider other)
{
Destroy(other.gameObject);
}
}
创建一个名为 PlayerScript 的新脚本,并把它附加到宇宙飞船上。
PlayerScript代码如下:
using UnityEngine;
using System.Collections;
public class PlayerScript : MonoBehaviour
{
//player speed
public float speed = 10f;
//bullet prefab
public GameObject bullet;
//Control Script
public GameControlScript control;
//player can fire a bullet every half second
public float bulletThreshold = .5f;
float elapsedTime = 0;
void Start() { }
void Update()
{
//keeping track of time for bullet firing
elapsedTime += Time.deltaTime;
//move the player sideways
transform.Translate(Input.GetAxis("Horizontal") * speed *
Time.deltaTime, 0f, 0f);
//spacebar fires. The current setup calls this "Jump"
//this was left to avoid confusion
if (Input.GetButtonDown("Jump"))
{
//see if enough time has passed to fire a new bullet
if (elapsedTime > bulletThreshold)
{
//fire bullet at current position
//be sure the bullet is created in front of the player
//so they don't collide
Instantiate(bullet, new Vector3(transform.position.x,
transform.position.y + 1.2f, -5f), Quaternion.identity);
//reset bullet firing timer
elapsedTime = 0f;
}
}
}
//if a meteor hits the player
void OnTriggerEnter(Collider other)
{
Destroy(other.gameObject);
control.PlayerDied();
Destroy(this.gameObject);
}
}
创建一个名为 BulletScript 的新脚本,并把它添加给子弹预设。
BulletScript代码如下:
using UnityEngine;
using System.Collections;
public class BulletScript : MonoBehaviour
{
float speed = 10f;
//Game Control Script
GameControlScript control;
void Start()
{
//Because this is instantiated, we must find
//the game control at run time
control = GameObject.Find("GameControl").GetComponent<GameControlScript>();
}
void Update()
{
//move upward
transform.Translate(0f, speed * Time.deltaTime, 0f);
}
//neither bullet nor meteor is a trigger, so we need
//to use a different collision method here
void OnCollisionEnter(Collision other)
{
Destroy(other.gameObject);
control.AddScore();
Destroy(this.gameObject);
}
}
滚动背景:
首先来讲述一下原理,准备两张连续的图片,我们使用正交投影的摄像机对准第一张背景,然后使用脚本让图片自上向下开始移动,当第一张图片移出摄像机的视野时,第二张图片立即接在第一张图的后面,继续向下移动,而当第二张图片移出摄像机视野时,第一张图片会立即接在第二张图片的后面,如此周而复始,就实现了连续滚动背景。
预设体:
prefabs 预设体是一个游戏物体及其组件的集合。
Unity prefab系统将游戏物体所有的组件,属性值和子物体作为一个可重复使用的资源,并且允许你新建,配置,存储这样一个游戏物体。预制资产充当模板,您可以在该模板中在场景中创建新的预制实例。
prefabs一般用在如非玩家角色(NPC),场景中的多个位置使用道具的或项目中的多个场景中使用的道具。
设置预设体和直接复制游戏物体的区别:
1、复制要求场景中先存在被复制的游戏对象,而预设体仅仅需要提前制作好预设体,场景中有无该游戏物体不重要。
2、复制后的游戏物体不会随着被复制物体的改变而改变,两者是相互独立,不受影响的,而预设体实例化的游戏物体会随着预设体的改变而改变,在一定条件下可以对预设体进行改变。
对Prefab所做的任何编辑都会自动反映在它的实例中,使您可以轻松地对整个项目进行广泛的更改,而无需对资产的每个副本重复进行相同的编辑。但对于实例,我们也可以对其进行覆盖,
使用Prefabs,可以在运行时实例化开始时不存在于场景中GameObjects, - 例如,在游戏过程中的恰当时刻出现的电,特效,射弹或NPC。
碰撞体、刚体和触发器:
碰撞体 (Collider) 与刚体 (Rigidbody) 结合使用可使 Unity 中的物理变得生动。刚体 (Rigidbody) 使对象可以受物理控制,而碰撞体 (Collider) 使对象可以相互碰撞。碰撞体 (Collider) 必须独立于刚体 (Rigidbody) 添加到对象。碰撞体 (Collider) 并不一定需要附加刚体 (Rigidbody),但是必须刚体 (Rigidbody) 才能使对象由于碰撞而移动。
当两个碰撞体 (Collider) 之间发生碰撞时并且如果其中至少一个附加了刚体 (Rigidbody),会将三条碰撞消息向外发送给附加到它们的对象。这些事件可以在脚本中进行处理,使您可以在使用或不使用内置 NVIDIA PhysX 引擎的情况下创建独特行为。
使用碰撞体 (Collider) 的备选方法是将其作为触发器 (Trigger),只需在检视视图(Inspector) 中选中“为触发器”(IsTrigger) 属性复选框即可。触发器 (Trigger) 实际上会被物理引擎忽略,并具有一组独有的三条触发器 (Trigger) 消息,这些消息会在与触发器 (Trigger) 发生碰撞时向外发送。触发器 (Trigger) 可用于触发游戏中的其他事件,如剧情画面、自动门打开、显示辅导消息等。
在本实验中,我制作了 Captain Blaster 游戏。首先设计了游戏元素,接着构建了游戏世界。构造了垂直滚动的背景,并使之运动起来。接下来构建了多个游戏实体,并通过编写脚本和控制对象添加了交互性。最后检查了游戏,并且探索了一些改进的方法。