查看: 4871|回复: 1
打印 上一主题 下一主题

[教程] Unity3D编写雷电游戏

[复制链接]
   

671

主题

1

听众

3247

积分

中级设计师

Rank: 5Rank: 5

纳金币
324742
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

跳转到指定楼层
楼主
发表于 2012-1-21 09:47:37 |只看该作者 |倒序浏览
基础实用的一篇经验教程!

一、搭建游戏的框架。
一般的游戏都可以分为四个场景:
1.开始界面
2.游戏场景
3.暂停界面
4.结束界面
开始界面,就是存放开始菜单的地方了,游戏场景就是游戏的主场景,游戏的主要元素都在这边体现,暂停和结束画面我就不多说了。更多的还有在开始和主游戏场景之间加入过场动画等等。当然你也可以在暂停界面中插入广告
我们会发现这几个场景之间其实就是切换来切换去的关系。如果知道设计模式中的State模式,就会发现跟这个很象。可以通过State模式来实现这几个场景的分离,然后分边为他们添加不同的元素。


想要挂接在unity3d中的结点的脚本都得继承MonoBehaviour,State就是所有的状态的父类了,主要是三个函数,Init()用于这个状态的初始化,Update用于场景的更新,Exit()用于当离开当前状态时所进行的一些操作,比如隐藏当前界面等。
创建一个空的Object用来挂接我们游戏的主脚本,代码如下:
using UnityEngine;
using System.Collections;

public class Main_Script : MonoBehaviour {
   
    public State m_CurState;
   
    public void ChangeState(State newState)
    {
        m_CurState.Exit();
        m_CurState = newState;
        m_CurState.Init();
    }

    // Use this for initialization
    void Start () {
        m_CurState.Init();
    }
   
    // Update is called once per frame
    void Update ()
    {
        m_CurState.Update();
    }
}
这样我们就可以轻松的在不同的状态中切换了。想要增加一个新的状态,就只需继承State类,然后在其中写这个状态所要的元素,在适当的地方ChangeState一下就好了。
脚本的层次结构如下:

例如我在按钮按下时切换到开始场景:
using System.Collections;
public class BeginGame : MonoBehaviour {
   
    public StateRun m_RunState;
    public Main_Script m_MainScript;

    // Use this for initialization
    void Start () {
    }
    // Update is called once per frame
    void Update () {
    }
    void OnMouseEnter()
    {
        print("enter");
    }
    void OnMouseUp()
    {
        m_MainScript.ChangeState(m_RunState);
    }
   
    void OnMouseExit()
    {
        print("exit");
    }
}
这个脚本是挂在一个板上面的,点击它时就进入游戏的主场景了。下回说一下飞机的移动。
现在开始真正的游戏元素的编写了。
第一步,让飞机动起来。
首先是飞机的前进,通常2D中的做就是背景的循环滚动。
在3D中我们可以让摄像机移动,背景我们可以做超一个大地形。。在地形上摆一些固定的东西。
    // Update is called once per frame
    void Update () {
        
        TurnLeft = false;
        TurnRight = false;

        if (Input.GetKey(KeyCode.W))
        {        
            Vector3 screenPos = Camera.mainCamera.WorldToScreenPoint(this.transform.position);
            //print(screenPos.y);
            if (Screen.height > screenPos.y)
                this.transform.Translate(Vector3.forward * Time.deltaTime * m_nMoveSpeed);
        }
        if (Input.GetKey(KeyCode.S))
        {
            Vector3 screenPos = Camera.mainCamera.WorldToScreenPoint(this.transform.position);
            if (0 < screenPos.y)
                this.transform.Translate(Vector3.forward * Time.deltaTime * -m_nMoveSpeed);
        }
        
        if (Input.GetKey(KeyCode.A))
        {
            Vector3 screenPos = Camera.mainCamera.WorldToScreenPoint(this.transform.position);
            if (0 < screenPos.x)
                this.transform.Translate(Vector3.left * Time.deltaTime * m_nMoveSpeed);
            //向左转
            if (CurRotation < RotateLimit)
            {
                print(CurRotation);
                CurRotation += RotateSpeed;
            }
            TurnLeft = true;
        }
        if (Input.GetKey(KeyCode.D))
        {
            Vector3 screenPos = Camera.mainCamera.WorldToScreenPoint(this.transform.position);
            if (Screen.width > screenPos.x)
                this.transform.Translate(Vector3.left * Time.deltaTime * -m_nMoveSpeed);
            //向右转
            if (CurRotation > -RotateLimit)
                CurRotation -= RotateSpeed;
            TurnRight = true;
        }
        //回归
        if (!TurnLeft && !TurnRight)
        {
            if (CurRotation > 0.0)
                CurRotation -=RotateSpeed;
            else if (CurRotation < 0)
                CurRotation +=RotateSpeed;
        }
        Quaternion rot = Quaternion.AngleAxis(CurRotation, new Vector3(0, 0, 1));
        m_Plane.rotation = rot;      
        //让相机和飞机一起以一定的速度前移
        this.transform.Translate(Vector3.forward * Time.deltaTime * m_nMoveSpeed);
        Camera.mainCamera.transform.Translate(Vector3.up * Time.deltaTime * m_nMoveSpeed);
   
    }
飞机的主要控制代码。。不知为什么,我的两个角度限制没有效果。。郁闷。。有空还看一下。。


这次先加入子弹的发射吧,没用模型,先用的一个capsule的prefab代替吧。
一想到各种武器之间的随意切换,就不由的想到了设计模式中的Strategy模式。
有关策略模式的详细介绍可以通过百度和维基来学习。
这个模式其实和State模式差不多。

Weapon类是所有武器的基类,新的武器继承于它,设置发射点,子弹模型,然后实现Fire函数就可以了。
WeaponManager用于管理所有武器,切换武器。

using UnityEngine;
using System.Collections;

public class WeaponManager : MonoBehaviour {
   
    public Weapon m_CurWeapon;
    private float m_FireElapseTime = 0;

    // Use this for initialization
    void Start () {
   
    }
   
    // Update is called once per frame
    void Update () {
        
        m_FireElapseTime += Time.deltaTime;
        if (Input.GetKey(KeyCode.Space))
        {
            if ( m_FireElapseTime > m_CurWeapon.GetBulletInterval())
            {
                m_CurWeapon.Fire();
                m_FireElapseTime = 0;
            }
            else
            {

            }
        }
    }
}
记录子弹发射后的时间,然后在大于时间间隔后才能发射下一个子弹。
武器父类:
using UnityEngine;
using System.Collections;
using System;

public class Weapon : MonoBehaviour {
   
    //子弹时间间隔
    protected float m_fBulletInterval = 0;
    //子弹类型
    public String m_typeName ="";
   
    public float GetBulletInterval()
    {
        return m_fBulletInterval;
    }

    // Use this for initialization
    void Start () {
   
    }
   
    // Update is called once per frame
    void Update () {
   
    }
   
    public virtual void Fire()
    {
        
    }
}

最基本的一种子弹:
using UnityEngine;
using System.Collections;

public class WeaponNormal : Weapon {
   
    private const int MAX_FP = 2;
    public Transform[] m_FirePoint = new Transform[MAX_FP];
    public Rigidbody m_BulletPre;
   
    WeaponNormal()
    {
        m_fBulletInterval = 2;
        m_typeName = "Normal";
    }
   
    // Use this for initialization
    void Start () {
   
    }
   
    // Update is called once per frame
    void Update () {
   
    }
   
    public override void Fire()
    {
        for(int i = 0; i < MAX_FP; i++)
        {
            Rigidbody clone = (Rigidbody)Instantiate(m_BulletPre, m_FirePoint.position,
                        m_FirePoint.rotation);
            
            clone.velocity = transform.TransformDirection(Vector3.forward * 20);
        }
    }
}
m_FirePoint是一个数组,存储了发射子弹的位置。在编辑器中,可以在飞机周围用空的GameObject放置一系列炮口的位置,然后拖入这个数组中。MAX_FP定义了炮口的数量。这里边用到了速度属性,这个是rigidbody才有的,所以在设置子弹的prefab时一定要为它加上rigidbody才行。
结构如下图所示:




最后的效果图:

现在子弹出来了,但是我们没有加上子弹的消亡,这样子弹被创建出来后就一直存在于场景中不会消失,会越积越多,所以我们让子弹在移出屏幕时就把他销毁掉。
using UnityEngine;
using System.Collections;
public class BulletControl : MonoBehaviour {

    // Use this for initialization
    void Start () {
   
    }
   
    // Update is called once per frame
    void Update () {   
        Vector3 screenPos = Camera.mainCamera.WorldToScreenPoint(this.transform.position);
            //print(screenPos.y);
        if (Screen.height < screenPos.y)
            Destroy(transform.gameObject);
    }
}
将这个脚本挂接在子弹的prefab上就可以实现效果了。
己方的飞机控制已经初步完成了,现在要加入敌机了。
理论上应该有两种方式:
1.预先设定,就是在编辑器里在你想要的位置上加上敌方的攻击单位
2.动态生成,根据游戏进行的时间来控制敌方攻击单位的产生。
这边采用第2个方式来产生,有一些地面单位倒是可以用第一种方式来产生。
我们首先要设置一个敌机的产生位置,用空的object可以随意的定一个坐标。
using UnityEngine;
using System.Collections;

public class EnemyManager : MonoBehaviour {
   
    public GameObject m_EnemyPre;
    public Transform m_CreatePoint;
   
    //一波飞机的数量
    public const int MAX_GROUP_ENEMYS = 4;
    //两波飞机之间的时间间隔
    public float GROUP_INTERVAL = 2.0f;
    //同一波中两架飞机之间产生的间隔
    public float CREATE_INTERVAL = 0.25f;
   
    private float m_fTimeAfterOneGroup = 0.0f;
    private float m_fTimeAfterCreate = 0.0f;
    private int m_nEnemyNum = 0;
    // Use this for initialization
    void Start ()
    {
   
    }
   
    // Update is called once per frame
    void Update ()
    {
        m_fTimeAfterOneGroup +=Time.deltaTime;
        m_fTimeAfterCreate += Time.deltaTime;
        
        if (m_fTimeAfterOneGroup > 2.0f)
        {
            if (m_fTimeAfterCreate > 0.25f)
            {
                GameObject clone = (GameObject)Instantiate(m_EnemyPre,
                                    m_CreatePoint.position, m_CreatePoint.rotation);
               
                clone.AddComponent("MoveScript_Shake");
                m_nEnemyNum +=1;
                m_fTimeAfterCreate = 0;
            }
            
            if (m_nEnemyNum == MAX_GROUP_ENEMYS)
            {
                m_fTimeAfterOneGroup = 0.0f;
                m_nEnemyNum = 0;
            }
        }
    }
}

敌机的移动脚本,可自己再定义新的移动脚本,在创建新敌机时挂上去就好:
using UnityEngine;
using System.Collections;
public class MoveScript_Shake : MonoBehaviour {
    //标志左右移动方向
    private int m_nDir = 1;
    //原始位置
    private Vector3 m_OriginalPos;
    //水平移动速度
    public float m_HoriSpeed = 0.1f;
    //翻转速度
    public float m_RotSpeed = 1.0f;
    //向前移动速度
    public float m_MoveSpeed = 0.0005f;
   
    public Vector3 curScreenPos;
   
    public Vector3 LDPoint;

    // Use this for initialization
    void Start ()
    {
        m_OriginalPos = transform.position;
        LDPoint = GameObject.Find("LDPoint").transform.position;
        print (LDPoint);
    }
   
    // Update is called once per frame
    void Update ()
    {
        float relativeOffset = transform.position.x - m_OriginalPos.x;
        
        if (Mathf.Abs(relativeOffset) > 5)
        {
            m_nDir = -m_nDir;
        }
        
        transform.Translate(new Vector3(m_nDir * m_HoriSpeed, 0, m_MoveSpeed));
        
        Transform planeTrans = transform.GetChild(0).transform;
        planeTrans.Rotate(0.0f, 0.0f, -m_nDir * m_RotSpeed);
        //出屏则消除
//        curScreenPos = Camera.mainCamera.WorldToScreenPoint(transform.position);
        //print("Cur:"+curScreenPos);
        if (transform.position.z < Camera.mainCamera.transform.position.z - 10)
        {
            Destroy(this.gameObject);
        }
//        print("Cur:"+transform.position);
//        if (transform.position.z < LDPoint.z)
//        {
//            Destroy(this.gameObject);
//        }
    }
}
实现效果:

原来那个用Unity3D自带的火焰老改不出想要的效果,去看了一个教程,调出一个还好的感觉。
         

           
最后的效果是:







本文转自:http://www.cnblogs.com/gameprogram/
分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

nts    

3

主题

1

听众

743

积分

初级设计师

Rank: 3Rank: 3

纳金币
7
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

沙发
发表于 2013-10-17 11:06:23 |只看该作者
精典的帖子啊,这么精典的游戏哦
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

手机版|纳金网 ( 闽ICP备2021016425号-2/3

GMT+8, 2024-11-18 22:49 , Processed in 0.399467 second(s), 28 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部