跟着教程做了一个space shooter:
【CSDN怎么传不了图了???!!!】
1、背景图
就是用一个quad,给这个quad添加一个material,material有texture。
2、导入Player模型
直接拖进hierarchy,然后添加粒子系统。
3、控制Player移动
PlayerController.cs:
void FixedUpdate()
{
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
Vector3 movement=new Vector3 (moveHorizontal,0.0f,moveVertical);
rb.velocity = movement*speed;
rb.position = new Vector3
(
Mathf.Clamp(rb.position.x,boundary.xMin,boundary.xMax),
0.0f,
Mathf.Clamp(rb.position.z,boundary.zMin,boundary.zMax)
);
rb.rotation = Quaternion.Euler (0.0f,0.0f,rb.velocity.x*(-tilt));
}
比较简单,用InputManager即可。speed是一个public变量,可以在Unity Editor里设置。为了让飞船不移出边界,用Clamp设置它的position属性。最后的rotation设置飞船旋转,这里涉及四元数和欧拉角,简单来说,四元数可以用来表示一个旋转,例如q=(a,b,c,d),其中a=cos(θ/2), b=xsin(θ/2), c=ysin(θ/2), d=zsin(θ/2),表示绕轴(x,y,z)、旋转角为θ的一个旋转。欧拉角同样可以表示旋转,旋转的规则是:首先绕着世界坐标系旋转α,然后绕着自身的坐标系x轴旋转β,最后绕着自身的坐标系z轴旋转γ。
4、子弹
新建一个空的GameObject,新建一个Quad作为它的孩子,用一个有贴图的material关联上去,当作子弹。注意渲染方式是Particles/Additive(这样黑色就被剔除掉了,因为黑色的rgb都是0,addtive表示叠加,0和任何东西叠加都是那个东西本身)
用下面的脚本让子弹动起来:
public class Mover : MonoBehaviour {
private Rigidbody rb;
public float speed;
void Start()
{
rb = GetComponent<Rigidbody> ();
rb.velocity = transform.forward * speed;
}
}
注意:forward的方向就是z的方向。
记得把子弹放到Prefabs里!Prefab放的是我们要在游戏场景中反复使用的东西。
(为什么要用一个GameObject来作为Quad的parent???如果直接用Quad也可以得到子弹啊???)
too simple
5、关联飞船和子弹
PlayerController:
void Update()
{
if (Input.GetButton ("Fire1") && Time.time > nextFire)
{
nextFire = Time.time + fireRate;
//GameObject clone=
Instantiate (shot, shotSpawn.position, shotSpawn.rotation);//as GameObject;
audio.Play();
}
}
这里有点难度了。在Player下面新建一个GameObject:ShotSpawn,但是要注意脚本里的ShotSpawn是一个Transform(为了简化,其实申明成GameObject也可以,但是这样还是要去访问它的transform,所以不如直接用transform)。
Fire1是在InputManager里定义过的,默认是鼠标左键点击。
注意脚本里的几个时间:Time.time,nextfire(初始化为0),和fireRate,Time.time获取从游戏开始到现在的时间,如果按下了鼠标并且当前时间比下一次开火的时间大,就更新nextFire,让它比Time.time略大,这样的效果就是连续按下鼠标不会出现“连弹”。
另一个值得注意的地方是Instantiate()函数,因为这里的子弹只要实例化出来就可以了,所以不需要用注释里的clone来引用它(但是如果我们确实要用一个引用来管理这个实例,在实例化的时候应该在后面加上as GameObject告诉Unity把它实例化成一个GameObject)。
6、销毁走出边界的实例
制造一个包围盒(就是一个Cube)围住可见的游戏区域。离开这个包围盒的实例就销毁:
public class DestroyByBoundary : MonoBehaviour {
void OnTriggerExit(Collider other)
{
Destroy (other.gameObject);
}
}
这主要是针对玩家射出的子弹。
7、制造陨石雨
新建一个GameObject,导入陨石的model。
如果陨石撞到子弹或者玩家会发生爆炸:
DestroyByContact.cs:
void OnTriggerEnter(Collider other)
{
if (other.tag == "Boundary")
{
return;
}
Instantiate (explosion,transform.position,transform.rotation);
if (other.tag == "Player") {
Instantiate (playerExplosion, other.transform.position, other.transform.rotation);
gameController.GameOver ();
}
gameController.AddScore (scoreValue);
Destroy (other.gameObject);
Destroy (gameObject);
}
注意这里的tag,如果没有tag,陨石在一开始就会爆炸。
做好一个陨石以后就可以做陨石雨了:
GameController.cs:
IEnumerator Spawnwaves()
{
yield return new WaitForSeconds (startWait);
while (true)
{
for (int i = 0; i < hazardCount; i++)
{
Vector3 spawnPosition = new Vector3 (Random.Range (-spawnVaules.x, spawnVaules.x), spawnVaules.y, spawnVaules.z);
Quaternion spawnRotation = Quaternion.identity;
Instantiate (hazard, spawnPosition, spawnRotation);
yield return new WaitForSeconds (spawnWait);
}
yield return new WaitForSeconds (waveWait);
if (gameOver)
{
restartText.text="Press 'R' for restart";
restart = true;
break;
}
}
}
做这里的时候要解决的问题是陨石之间的碰撞,用spawnWait来给每次实例化一个间隔,这样就能避免陨石之间的碰撞了。
startWave是给玩家的准备时间,waveWait是每一波陨石雨的间隔。
8、添加音效
比较简单,略。
9、计分
这是第一次遇到在两个脚本之间传数据的情况。在GameController中初始化Text和分数,每次击毁陨石得分的逻辑应该写在DestroyByContact里。
在视频中出现的一个问题:DestroyByContact是添加到Prefab中的Asteroid上的,不能直接把Hierarchy Window中的Game Controller拖到槽中,视频中的解释:
prefabs are templates of game objects that can be instantiated in any scene in our game,it doesnt make sense that a template that can be added to any scene in our game can hold a refrence to an instance in just one scene
子弹被实例化以后可以拥有game controller的实例的reference
private GameController gameController;
void Start()
{
GameObject gameControllerObject = GameObject.FindWithTag ("GameController");
if (gameControllerObject != null)
{
gameController = gameControllerObject.GetComponent<GameController> ();
}
else
{
//do nothing here...
}
}
10、重新开始游戏
GameController.cs中的主要代码:
void Update()
{
if (restart)
{
if (Input.GetKeyDown (KeyCode.R))
{
Application.LoadLevel (Application.loadedLevel);//reload the scene
}
}
}
后续会添加敌人......