(PS:本文为个人原创旧文章,原发布地址为:CSDN )
【摘要】往后的几篇博客,sunset会将以前自己学习过程中独立开发的一些小游戏或者小demo陆续做成几篇小教程公布出来,其中有十分简单但有趣的小游戏,也不乏比较深奥的一些demo代码。sunset会在疑难点进行比较仔细的注释。这篇就先来个简单的空战游戏。
首先,先介绍我们所需要的资源文件:1)战斗机;2)背景图;3)障碍物或者说敌人战斗机;4)子弹特效5)合适的音频文件(最好是ogg格式,稍后解释),嗯,差不多就是这样。
1.创建战斗场景##
1)将战机模型拖入Hierachy视窗中,让其显示在Scene视图中,将其transform进行reset,添加MeshCollider组件(能不用最好不同这个组件,对性能消耗大,但是这个游戏很小,性能问题可忽略所以可以用),然后制作预制物体(prefabs),之后将reset Camera的transform,将Camera的投影方式改为正交方式,然后使其位于战斗机上方,使战斗机能够在game视图中以合适的大小显示。然后在Hierarchy视图中创建一个Quad(四方面),将背景图片直接拖放到Quad上面,改名为BackGround,修改背景图的合适大小以符合Camera的视图,再修改背景图的位置,使其位于战斗机下方,然后制成预制物体。
2)之后在背景图的四个方向边缘制作几个Cube,改名为Boundary(1,2,3,4)。修改合适大小,主要用于判定子弹或者敌人没有受到碰撞的时候,撞击到边界就进行淘汰(destroy)。
3)使用光照,建立三个Direcitonal Light,分别为:主光照,辅助光照,边缘光照。主光照用与整体的照明效果,辅助光照用于对主光照进行补充,边缘光照用于照明边缘,使边缘更加明显。
2.脚本代码
1.首先是战斗机的移动代码:
[System.Serializable]
public class Bullets
{
public GameObject SimpleFireBullet;
}
[System.Serializable]
public class Boundary
{
public float yMin, yMax, xMin, xMax;
}
public class PlayerController : MonoBehaviour
{
private Rigidbody rigidbody;
public Boundary boundary;
public Bullets bullets;
private float NextFire;
public Transform SpawnPoint;
public float Speed;
public float RateTime;
public float Tilt;
public GameObject PlayerExplosion;
void Awake()
{
rigidbody = this.GetComponent<Rigidbody>();
}
void Update()
{
if(Input.GetKey(KeyCode.J) && (Time.time > NextFire))
{
NextFire = Time.time + RateTime;
GameObject CloneBullet = Instantiate(bullets.SimpleFireBullet, SpawnPoint.position, SpawnPoint.rotation) as GameObject;
}
if(GameController.LifeCount <= 0)
{
GameController.GameOver = true;
}
}
void FixedUpdate()
{
float H = Input.GetAxis("Horizontal");
float V = Input.GetAxis("Vertical");
Vector3 move = new Vector3(H, V , 0);
rigidbody.velocity = move * Speed;
rigidbody.position = new Vector3(Mathf.Clamp(rigidbody.position.x, boundary.xMin, boundary.xMax), Mathf.Clamp(rigidbody.position.y, boundary.yMin, boundary.yMax), 0.0f);
rigidbody.rotation = Quaternion.Euler(rigidbody.rotation.x,180.0f + -1 * rigidbody.velocity.x * Tilt, rigidbody.rotation.y);
}
void OnTriggerEnter(Collider _collider)
{
if(_collider.gameObject.tag == "Enemy")
{
Destroy(_collider.gameObject);
Destroy(this.gameObject);
GameObject CloneExplosion = Instantiate(PlayerExplosion, this.transform.position, Quaternion.identity) as GameObject;
if(CloneExplosion)
{
Destroy(CloneExplosion, 5);
}
GameController.LifeCount -= 1;
GameController.Dead = true;
}
}
}
简单解释一下脚本,就是通过水平轴和垂直轴的量性变化修改刚体的向量(velocity )来进行移动,这里使用Mathf.clamp()函数来限制移动的最大边缘距离和最小边缘距离,有关这个函数的使用方法可以在UnityAPI文档中进行查找。然后希望在左移或者右移的时候战机有一定的倾斜,所以使用Quaternion.Euler函数通过一个Parameter:Tilt来调整左右移动时的倾斜角度。具体代码如上。
然后设定按键用于发射导弹并限定每秒导弹的可发射量(设定RateTime为0.25时,每秒可发射4次导弹)其他的OnTriggerEnter(Collider _collider)函数是用于检测机体是否碰撞敌人或者障碍物以及碰撞一定次数后游戏结束。具体使用方法查询API。
2.子弹的移动代码:
using UnityEngine;
using System.Collections;
public class BulletsMove : MonoBehaviour
{
private Rigidbody rigidbody;
public float BulletSpeed;
public GameObject BulletExplosion;
void Awake()
{
rigidbody = this.GetComponent<Rigidbody>();
}
void FixedUpdate ()
{
if(this)
{
rigidbody.velocity = new Vector3(0.0f, 1, 0.0f) * BulletSpeed;
}
}
void OnTriggerEnter(Collider _collider)
{
if(_collider.gameObject.tag == "Enemy")
{
GameObject CloneExplosion = Instantiate(BulletExplosion, _collider.gameObject.transform.position, Quaternion.identity) as GameObject;
if(CloneExplosion)
{
Destroy(CloneExplosion, 3);
}
Destroy(this.gameObject);
Destroy(_collider.gameObject);
GameController.Score += 10;
}
}
}
比起战机,子弹移动代码较为简单,同样使用刚体,代码简单,不一一赘述。OnTriggerEnter(Collider _collider)用于子弹碰撞敌人后,消灭敌人,消灭自己(注意:destroy是在该帧的结束时进行判定的,所以无需在乎代码先后问题。)
3.边界的淘汰判定代码:
using UnityEngine;
using System.Collections;
public class BoundaryTrigger : MonoBehaviour
{
void OnTriggerEnter(Collider _collider)
{
Destroy(_collider.gameObject);
}
}
不解释。。。
4.创造敌人以及实现无限循环的代码:
using UnityEngine;
using System.Collections;
[System.Serializable]
public class Ship
{
public GameObject Ship1;
public Transform SpawnShip;
}
public class GameController : MonoBehaviour
{
public Ship _ship;
public GameObject AST;
public Vector3 SpawnTransform;
public int WaveCount;
public float StartWaitTime;
public float WaveWaitTime;
public float SpawnWaitTime;
public static bool GameOver = false;
public static bool Dead = false;
public static int LifeCount = 5;
public static int Score;
private AudioSource _audio;
void Awake()
{
_audio = this.GetComponent<AudioSource>();
}
void Start()
{
Time.timeScale = 1;
StartCoroutine(SpawnWave());
}
void Update()
{
if(GameOver)
{
_audio.Stop();
}
if(Dead)
{
//new WaitForSeconds(2.0f);
GameObject CloneShip1 = Instantiate(_ship.Ship1, _ship.SpawnShip.position, _ship.SpawnShip.rotation) as GameObject;
Time.timeScale = 1;
Dead = false;
}
if(Time.timeScale == 0)
{
//_audio.Pause();
}
else if(Time.timeScale == 1)
{
//_audio.Play();
}
}
IEnumerator SpawnWave()
{
yield return new WaitForSeconds(StartWaitTime);
while(true)
{
for(int i = 0; i < WaveCount; i++)
{
Vector3 SpawnPosition = new Vector3(Random.Range(-SpawnTransform.x, SpawnTransform.x), SpawnTransform.y, SpawnTransform.z);
GameObject CloneAST = Instantiate(AST, SpawnPosition, Quaternion.identity) as GameObject;
yield return new WaitForSeconds(SpawnWaitTime);
}
yield return new WaitForSeconds(WaveWaitTime);
}
}
}
其实这个代码只能作为一种参考,毕竟真实去制作一款这样的游戏时,敌人的数量和种类以及出现方式,攻击方式都是互不相同的。
这里主要学习一下这种通过Start()函数运用协程来无限循环创造敌人的思路与实现方式就好。
5.最后是创建UI面板显示分数,存活次数等等信息,看个人兴趣与能力了,美工好的可以做的十分华丽,普通的程序员只要能用就行了啦。然后添加音频文件,将音频文件拖放到子弹的预制物体上,勾选PlayOnAwake即可,然后添加主背景音效到GameController脚本所附加的空物体上就可,设定Loop。
这样,一款简单的2D空战游戏就完成了,最后是运行的效果:
补充:该游戏文件包下载地址:http://pan.baidu.com/s/1o6vF2Ca
原创文章,转载请注明出处( https://www.cnblogs.com/Beyond1900/p/14484969.html )