您的位置:首页 > 移动开发 > Unity3D

[2015/09/08] Unity3D飞机大战(简陋版) Practice

2015-09-08 11:09 579 查看
自己学的还是太差,进度很慢,不停地查各种资料。

1 2D背景实现自动滚动

1.1 插入2D背景

直接插入图片就好,我还在想sprite到底是用来做什么的……

3D摄像头改为2D,Orthographic(正交投影模式)。它的初始位置我没动,就是 (0,0,-10)

1.2 自动滚动

忙了一早上,其实就实现了这个,自己找的图片,要修改参数,我又是个强迫症……

以后我就有经验了:

第一步,将三张一模一样的图片拖进Scene中。上下排好,中间那张图片的y坐标最好是0,否则就会像我这样……不停地调……如果中间不是0,可以算,想算吗少年:)

第二步,看最上面那张图片的position.y,和最下面那张图片的position.y

第三步,根据标注出来的颜色对应填好即可

using UnityEngine;
using System.Collections;

public class BackgroundRoll : MonoBehaviour {
//背景滚动速度
public float speed = 5f;

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {
transform.Translate(Vector3.down * Time.deltaTime * speed);
if(transform.position.y <-11.27f){
transform.position = new Vector3(0,11.33f,0);
}
}
}


2 处理飞机的移动和炮弹的产生

2.1 飞机的移动

using UnityEngine;
using System.Collections;

/*
* 第一步,处理飞机的移动
*
*/
public class PlaneBehavior : MonoBehaviour {

public float p_speed;
// Use this for initialization
void Start () {
p_speed = 3.0f;
}

// Update is called once per frame
void Update () {

//飞机的移动
if(Input.GetKey(KeyCode.UpArrow)){
transform.Translate(Vector3.up * Time.deltaTime * p_speed);
}else if(Input.GetKey(KeyCode.DownArrow)){
transform.Translate(Vector3.down * Time.deltaTime * p_speed);
}
if(Input.GetKey(KeyCode.LeftArrow)){
transform.Translate(Vector3.left * Time.deltaTime * p_speed);
}else if(Input.GetKey(KeyCode.RightArrow)){
transform.Translate(Vector3.right * Time.deltaTime * p_speed);
}
}


2.2 控制边界

void check(){
//检测边界,使飞机不飞出摄像机
if (transform.position.x <= -2.4f)
{
transform.position = new Vector3(-2.4f, transform.position.y, 0);
}
if (transform.position.x >= 2.4f)
{
transform.position = new Vector3(2.4f, transform.position.y, 0);
}
if (transform.position.y >= 4.2f)
{
transform.position = new Vector3(transform.position.x, 4.2f, 0);
}
if (transform.position.y <= -4.27f)
{
transform.position = new Vector3(transform.position.x, -4.27f, 0);
}

}


2.3 子弹的产生和子弹目前需要绑定的脚本

先预设一个Bullet的Prefab,然后给它绑定两个脚本,一个是AutoDestroy,这个就不写了,实现的功能是离开摄像头范围自动消除GameObject;第二个是BulletBehavior,当子弹射出后就一直向前走。

飞机的脚本中添加以下内容:

//空格键发射子弹
if (Input.GetKeyDown(KeyCode.A))
{

GameObject bullet = Resources.Load("Bullet") as GameObject;
Instantiate(bullet,this.transform.position,this.transform.rotation);

}


using UnityEngine;
using System.Collections;

public class BulletBehavior : MonoBehaviour {
public float bulletSpeed = 4.0f;
// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {
transform.Translate( Vector3.up * Time.deltaTime * bulletSpeed);
}
}


这是今天的进度。整个下午加晚上都在做高频课设的东西,不停的算、画、调,这还只是个最初的开始——Multisim仿真...

明天预计实现敌机的出现和处理炮弹的碰撞什么的。。

3 敌机的处理

3.1 新建敌机预设

新建了三种预设,都是从网上找的图片,怎么创建预设呢?

第一步,Create - Prefab,新建出来的是白色的。

第二步,找一个Sprite(精灵)拖到那个New prefab上,就变成了蓝色。

第三步,在inspector的sprite中设置自己的图片。

第四步,绑定你需要的脚本,如一个敌机所需要的 EnemyBehavior(Script), Box Collider2D,AutoDestroy(Script),Rigidbody2D

3.2 敌机Component说明

1] Box Collider2D
这就是一个普通的碰撞边界,不过这个是2D的,Box在视图中似乎是一个方的边界。

如果要实现碰撞检测的话,会调用OnTriggerEnter2D(Collider2D)函数,一定要记得不要打错函数名啊~MonoDevelop最糟心的就是不像Eclipse很多出错它根本不提醒。。然后你就自己不知道哪里错了慢慢调。。

调用这个函数记得把 IsTrigger 选项勾上。

2] Rigidbody2D
也是一个系统自带的2D脚本,不带刚体属性的话是无法实现碰撞的。

那么需要注意的一点就是,在本游戏中没什么重力的概念,所以把GravityScale设为0。

3] AutoDestroy
这个没什么好说的啦,离开摄像头视野范围自动销毁。

4] EnemyBehavior
作为敌机有一些行为,比如自动向下降,生命,碰到玩家飞机给玩家飞机减生命啊什么的(由于我是小白,所以就不设定敌机发子弹了,但是肯定是可以实现的~)。脚本如下:

using UnityEngine;
using System.Collections;

public class EnemyBehavior : MonoBehaviour {
//生命值
public int enemyLife;
//敌机速度
public float e_speed;
//爆炸动画
//private Animator animator;

// Use this for initialization
void Start () {
//animator = GetComponent<Animator>();
}

// Update is called once per frame
void Update () {
transform.Translate(Vector3.down * Time.deltaTime * e_speed);

}

public void Hit(int power){
if(enemyLife > 0){
enemyLife -= power;
}else{
Destroy(this.gameObject);
GameObject.Find("Main Camera").GetComponent<Logic>().score += 100;
//Instantiate(Explosion,transform.position,Quaternion.Euler(new Vector3(0,0,0)));
}

}

void OnTriggerEnter2D(Collider2D mCollider){
//敌机与主机碰撞双方掉血
if(mCollider.gameObject.tag == "Player"){
//敌机掉血
this.Hit(2);
mCollider.gameObject.GetComponent<PlaneBehavior>().Hit (1);
//			if(mCollider.gameObject.GetComponent<PlaneBehavior>().PlayerLife<=0){
//				gameover();
//			}
}
}

void gameover(){
Application.LoadLevel("gameover");
}
}


暂时还没有处理爆炸效果,明天或者后天有空了再做。。

3.3 敌机发生器

在界面最上建一个新的GameObject,绑定脚本让它自动根据三个预设生成敌机。

using UnityEngine;
using System.Collections;

public class EnemySpawn : MonoBehaviour {
//定义产生敌人速度
public float SpawnTime = 1.0f;
//定义敌人对象数组
private GameObject[] Enemys = new GameObject[3];

// Use this for initialization
void Start () {
GameObject Enemy1 = Resources.Load("Enemy1") as GameObject;
GameObject Enemy2 = Resources.Load("Enemy2") as GameObject;
GameObject Enemy3 = Resources.Load("Enemy3") as GameObject;
Enemys[0] = Enemy1;
Enemys[1] = Enemy2;
Enemys[2] = Enemy3;
//协程是一个分部执行,遇到条件(yield return 语句)会挂起,直到条件满足才会被唤醒继续执行后面的代码
StartCoroutine("Spawn");
}

// Update is called once per frame
void Update () {

}

IEnumerator Spawn(){
//等待
yield return new WaitForSeconds(SpawnTime);
//克隆对象
int x = Random.Range(0,3);
//减少2 飞机出现的次数
if(x==1){
x = Random.Range(0,3);
}
Vector3 position = new Vector3(2.5f * (float)Random.Range(-1f,1f),
transform.position.y,
-1 );
Instantiate(Enemys[x],
position,
Quaternion.Euler(0,0,0));
StartCoroutine("Spawn");
}
}


在写这个的时候出了一个问题,那就是数组越界,然而并没有越界——

Array index is out of range
Make the array private

Unity will use the inspector version of the array for any public arrays, and since you probably didn't fill values in there, it'll assign a 0 length array

privatevar vertices
:Vector3[]=
newVector3[10];


解决方案如上,将数组变成Private就好了。

3.4 修改子弹脚本和预设

之前我的子弹预设居然是Capsule,也就是3D的,所以怎么碰撞都检测不到好吗。。真是给自己的智商跪了!

新建一个预设,自己用ps画了一个子弹(心疼自己,每次都是又当美工又当程序员),然后绑定脚本

Bullet Behavior / AutoDestroy / Rigidbody 2D / Edge Collider

为什么用Edge Collider呢,是因为如果用 Box的话,子弹还没碰到飞机就会被框框挡住了。。应该是原始预设超出了子弹本身的尺寸的缘故。

using UnityEngine;
using System.Collections;

public class BulletBehavior : MonoBehaviour {
//子弹速度
public float bulletSpeed = 4.0f;
//子弹威力
public int power = 2;
//玩家飞机
private GameObject Player;

// Use this for initialization
void Start () {
//Player = GameObject.Find("Player");
}

// Update is called once per frame
void Update () {
transform.Translate( Vector3.up * Time.deltaTime * bulletSpeed);
}

//碰撞检测(注意这里是2D碰撞)
void OnTriggerEnter2D(Collider2D mCollider){
if(mCollider.gameObject.tag=="Enemy"){
//敌人减血
mCollider.gameObject.GetComponent<EnemyBehavior>().Hit(power);
//销毁子弹
Destroy(this.gameObject);
}

}

}


4 得分显示

直接上脚本吧,这个比较简单。

using UnityEngine;
using System.Collections;

public class Logic : MonoBehaviour {

GameObject plane;
public int score;

// Use this for initialization
void Start () {
plane = GameObject.Find("Player");
}

// Update is called once per frame
void Update () {

}
void OnGUI(){
GUILayout.Box("当前飞机生命" + plane.GetComponent<PlaneBehavior>().PlayerLife);
GUILayout.Box("当前得分" + score);
}
}


5 爆炸动画

5.1 Animation Clip的制作

导入动画的一组图片(其实就是帧动画),然后将他们全部拖入Hirarchy中,系统就会自动生成一个.anim文件,保存。同时生成了一个animation controller,这个等会儿再说。

保存以后可以在Animation里面编辑这个动画,比如每一帧是什么样子的,间隔多少,都可以调整。界面一开始确实有点难懂,事先也没看过这方面视频,就看着MOMO的几篇文章,自己东拼西凑弄出来了,不算很复杂。

5.2 Animation controller

这个controller其实就是状态机。状态机是什么意思呢?就是概括了某个GameObject的所有状态,并且阐述所有状态之间的转换(及其条件)的东西(语文学得好:)呸真不要脸)。

首先默认的是有一个Any State,很好理解,就是任何状态都是这个状态;

然后有一个default状态,就是一开始所处在的状态,你可以设置为Idle(项目中就是Idle啦,记得motion是none,否则一上来就会播放动画)。

最后就是爆炸状态,设置为Explosion,motion是要播放的Animationclip,项目中就是explosion。选一下就好了。

接下来阐述状态之间的转换。

Idle → Explosion 条件是Parameter中的参数 isLive 为 false

Explosion → Destroy 条件是 Exit Time > 0.9

设置好了以后状态机就完成了。然后写脚本,这个是敌机的最终脚本了。

using UnityEngine;
using System.Collections;

public class EnemyBehavior : MonoBehaviour {
//生命值
public int enemyLife;
//敌机速度
public float e_speed;
//爆炸动画
private Animator animator;
public bool isLive;
public AudioSource explosion;

// Use this for initialization
void Start () {
animator = GetComponent<Animator>();
explosion = gameObject.GetComponent<AudioSource>();
animator.SetBool("isLive", true);
isLive = true;
}

// Update is called once per frame
void Update () {
transform.Translate(Vector3.down * Time.deltaTime * e_speed);
//获取动画层 0 指Base Layer.
AnimatorStateInfo animatorState = animator.GetCurrentAnimatorStateInfo(0);
//销毁敌机
if (animatorState.IsName("Base Layer.Destroy"))
{
Destroy(this.gameObject);
}
}

public void Hit(int power){
if(enemyLife > 0){
enemyLife -= power;
}else{
isLive = false;
//Destroy(this.gameObject);
GameObject.Find("Main Camera").GetComponent<Logic>().score += 100;
animator.SetBool("isLive", false);
explosion.Play();
}

}

void OnTriggerEnter2D(Collider2D mCollider){
//敌机与主机碰撞双方掉血
if(mCollider.gameObject.tag == "Player" && isLive){
//敌机掉血
this.Hit(2);
mCollider.gameObject.GetComponent<PlaneBehavior>().Hit (1);

}
}
}

}


打算之后去看一下关于动画讲解的视频,这方面看了很多文章查了资料,但是都没有很好的理解,姑且算是这样吧,起码Plane的爆炸效果我就没做好……

6 其它处理

其它处理包括什么 声音的播放、本地存储最高分、GUI的生命/分数显示、游戏结束界面、游戏开始界面什么的就不赘述了,相对来说也比较简单,而且GUI也不是这个项目的重点。花了几天时间一边学一边做这个项目,感觉也是对U3D明白了更多,但是实在还是远远不够,那么继续加油努力吧:)

Ps: 只有这个没有拉黑了吧,估计自己也忘了。昨天去实验室收拾东西,又不争气地哭了。我会继续努力,不管你怎样想,也祝你成功:)

2015/09/10
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: