Unity經典游戲教程之:松鼠大作戰2


版權聲明:

  • 本文原創發布於博客園"優夢創客"的博客空間(網址:http://www.cnblogs.com/raymondking123/)以及微信公眾號“優夢創客”(微信號:unitymaker)
  • 您可以自由轉載,但必須加入完整的版權聲明!

松鼠大作戰游戲制作

  1. 游戲介紹 1990年,經迪士尼授權,由日本卡普空(Capcom)電視游戲公司制作的基於任天堂FC主機的電視游戲《松鼠大作戰》出版發行。游戲延用迪士尼動畫片《松鼠大作戰》里的兩只可愛花栗鼠Chip and Dale(奇奇和蒂蒂),從尋找小貓咪的委托任務開始,到擺脫肥貓陷阱與其一決高下為故事內容。可單人玩又可雙人對戰,可互助又可互攻,流暢度,創造力與可玩性均為同類游戲中的領軍者。
    1993年,卡普空制作發行了《松鼠大作戰2》再一次將經典搬到屏幕。除了制作更加精良外,故事也更加驚險刺激。
    pXQxk6.jpg

  2. 場景搭建 將場景中的圖片首尾拼接,在合適的位置設置碰撞器與平台控制器
    pXlknA.md.png

  3. 主角Chip登場 將主角圖片拖入層級面板上,命名為“player”,添加剛體2D組件和合適的碰撞器組件。為player設置子節點hand,添加碰撞器組件設置為觸發,用於觸碰箱子的判斷;設置子節點foot,調整恰當的位置,用於跳躍,以及主角跳躍動畫的條件判定;再設置兩個節點用於主角投擲物品發射的位置。pXlIHI.md.png

  4. 主角Chip動畫制作 打開動畫控制器,創建主角的Idel動畫,在合適的時間軸拖上相應的主角圖片,重復操作將主角的動畫設置好。

  5. pXGvWV.md.png

  6. 主角Chip基本動作實現 為player添加腳本組件,命名為PlayerController,主角的移動需要通過輸入設備為其提供指令,並進行相應的操作,Unity中Input可以獲得這些操作。horizontal與vertical均是浮點數范圍為-1至1,方向與卡迪爾坐標相同,isPressjump與isPressFire均為Bool類型。通過射線的方式我們可以判斷處人物是否在跳躍狀態下。Chip的移動與跳躍我們通過物理運動進行操作,在FixedUpdate()下進行代碼操作;

isJump = !(Physics2D.Linecast(transform.position, foot.position, 1 << LayerMask.NameToLayer("Map")) || Physics2D.Linecast(transform.position, foot.position, 1 << LayerMask.NameToLayer("Box")));
horizontal = Input.GetAxis("Horizontal");//上下
vertical = Input.GetAxis("Vertical");//左右
isPressjump = Input.GetButtonDown("Jump");//跳躍
isPressFire = Input.GetButtonDown("Fire1");//攻擊
void FixedUpdate()
    {
        if (isControl)
        {
            if (!isDown && isPressjump && !isJump)
            {
                if (rig.velocity.y < 1F)
                {
                    rig.AddForce(Vector3.up * force);
                }
                isJump = true;
            }
            if (isDown)
            {
                rig.velocity = new Vector3(0, rig.velocity.y);
            }
            else
            {
               rig.velocity = new Vector3(speed * horizontal, rig.velocity.y);
            }
        }
    }
  1. 主角Chip攻擊實現 與射擊游戲的區別,在本作中,CHip是通過搬物品再投擲出去進行攻擊的。這里的設想是將場景的箱子與投擲的箱子區別開來,方便判斷。player下的節點hand添加腳本,在OnTriggerStay2D下調用MoveBox方法,isHandBox判斷手中是否有箱子,isThrow則判斷有沒有進行投擲操作。這里貼出主要代碼:
    public void MoveBox(Collider2D collision)
    {
        //搬箱子
        if (!isDown && collision.tag.StartsWith("Box") && isPressFire && !isHandBox && !isThrow)
        {
            Destroy(collision.gameObject);//場景中的箱子被銷毀了,游戲中松鼠頭頂上的箱子僅僅是外觀的區別
            isHandBox = true;
            animator.SetTrigger("moveBox");
            Instantiate(movebox);
        }
    }
    private void ThrowBox()
    {
        if (isHandBox && isPressFire)
        {
            isHandBox = false;
            isThrow = true;
            GameObject o = Instantiate(throwingbox);//投擲箱子時實例化一個投擲的箱子(與場景的箱子功能不同)
            if (!isDown)
            {
                o.transform.position = transform.Find("fireposition").position;
            }
            else
            {
                o.transform.position = transform.Find("downfireposition").position;
            }
            o.GetComponent<ThrowingBox>().Throw(transform.localScale.x, vertical);
            StartCoroutine(ResetIsThrow());
            animator.SetTrigger("throwbox");
            Instantiate(throwbox);
        }
    }
  1. 主角Chip動畫控制器動畫控制 給出合適的條件進行動畫轉換,本作動作豐富,通過多次嘗試,本作動作才比較連貫。pXriiF.md.png

  2. 場景箱子的制作 將箱子圖片拖進層級面板中,為它設置合適的碰撞器,做成預制體備用。pXsn6s.png

  3. 投擲箱子的制作 與場景箱子類似,設置觸發器,另外需要增加剛體2D組件與腳本,腳本主要用於處理與怪物接觸的交互,同樣也制成預制體。pvrYe1.png

  4. 其他道具的制作 其他道具有花(吃了一定的數量可以獎勵生命);蜂蜜(蜜蜂怪物攻擊的道具,玩家觸碰會造成傷害);堅果(若主角受傷,吃到該道具會增加1點Hp值);叉子(投擲小鼠投擲的道具,玩家觸碰會造成1點Hp傷害)。pvr6eI.png

  5. 一些其他的制定 在本場景的末尾添加添加合適的觸發器,綁上腳本可以通過下一個場景;在懸崖處也添加一個長條的觸發器,用於觸發主角死亡判定並重新加載本場景。

  6. 蜜蜂怪物的制作 在層級面板中,拖進一張蜜蜂怪物的圖片,添加剛體組件,和合適的碰撞器,設置為觸發。為這個對象設置兩個動畫,一個為停止動畫,一個為飛行動畫。攝像機還沒有看到蜜蜂時,蜜蜂處於靜止狀態,當蜜蜂被攝像機照到時,蜜蜂處於飛行狀態。蜜蜂飛到玩家的X位置時,投擲蜂蜜道具。附上蜜蜂的物理運動時腳本代碼:pvs9mR.md.pngpvyGUx.md.png

    public void FixedUpdate()
    {
        if (isHit)
        {
            rig.velocity = new Vector2(masterFlyX, masterFlyY);
        }
        else if (isRest)
        {
            rig.velocity = Vector2.zero;
        }
        else if (!isStop && !isHit)
        {
            rig.velocity = new Vector2(flyspeedx, flyspeedy);
            if (this.transform.position.x < PlayerController.instance.transform.position.x && honeyNum > 0)
            {
                Attack();
                honeyNum--;
                isRest = true;
                StartCoroutine(ResetIsRest());
            }
        }
    }
  1. 投叉小鼠的制作 與蜜蜂怪物類似,添加剛體組件和合適的碰撞器。為這個對象設置四個動畫,停止動畫,巡邏動畫,攻擊動畫,跳躍動畫。給這個對象加一個點進行射線是不是發現玩家,發現時,執行攻擊邏輯播放攻擊動畫。這里附上部分代碼:pvHJIO.md.pngpvHazd.md.png
public void Update()
    {
        if (state == State.Stop && this.transform.position.x < cam.position.x + xoffset)
        {
            //怪物行為被激活
            state = State.Walk;
            animator.SetTrigger("Walk");
        }
        else if (this.transform.position.x < cam.position.x - xoffset || this.transform.position.y > cam.position.y + yoffset)
        {
            Destroy(this.gameObject);
        }
        Walk();
        Throw();
        Escape();
    }
  1. Boss制作 在層級面包板中拖入Boss的圖片,組件與合適的觸發器,Boss主要兩種狀態,添加剛體,走動狀態與停下攻擊狀態。這里附上Boss的代碼:pvqdbt.md.pngpvq55T.md.png

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Boss : MonoBehaviour {
    
        public float range;
        public float speed;
        public float stopTime;
        public float walkTime;
        private float dir = 1;//方向正1表示坐標向右
        private float leftrange;
        private float rightrange;
        private Rigidbody2D rig;
        private Animator ani;
        private SpriteRenderer spriterender;
        private State state = State.Stop;
        private float currentTime;
        public GameObject puke;
        public float pukeSpeed;
        public int hp;
        public int totalHp;
        private bool isFlash = false;
        public float flashTime;
        public Color flashColor;
        public Color normalColor;
        public Color dangerColor;
        public float destoryTime;
    
        public GameObject bosspuke;
        public GameObject kill;
        
    
        public enum State
        {
            Stop,
            Walk,
            Attack,
            Die
        }
    	// Use this for initialization
    	void Start () {
            rig = this.GetComponent<Rigidbody2D>();
            ani = this.GetComponent<Animator>();
            spriterender = this.GetComponent<SpriteRenderer>();
            leftrange = this.transform.position.x - range;
            rightrange = this.transform.position.x + range;
    	}
    	
    	void Update () {
    
            ChangeColor();
            ani.SetInteger("state", (int)state);
            if (state == State.Stop)
            {
                StartCoroutine(CancelStop());
            }
            else if (state == State.Walk)
            {
                currentTime += Time.deltaTime;
                if (currentTime > walkTime)
                {
                    currentTime = 0f;
                    state = State.Attack;
                }
                else
                {
                    if (this.transform.position.x > rightrange)
                    {
                        transform.localScale = new Vector3(-1F, 1F, 1F);
                        dir = -1F;
                    }
                    if (this.transform.position.x < leftrange)
                    {
                        transform.localScale = new Vector3(1F, 1F, 1F);
                        dir = 1F;
                    }
                }
            }
            else if (state == State.Die)
            {
                ani.enabled = false;
                this.GetComponent<Collider2D>().enabled = false;
                Destroy(this.gameObject, destoryTime);
            }
    	}
    
        public void ChangeColor()
        {
            if (isFlash)
                spriterender.color = flashColor;
            else
                spriterender.color = normalColor;
            if (hp <= 1)
            {
                spriterender.color = dangerColor;
                if (hp <= 0)
                {
                    state = State.Die;
                }
            }
        }
    
        public void FixedUpdate()
        {
            if (state == State.Stop || state == State.Attack)
            {
                rig.velocity = Vector2.zero;
            }
            else if (state == State.Walk)
            {
                  rig.velocity = new Vector2(dir * speed, 0);
            }
            else if (state == State.Die)
            {
                rig.velocity = new Vector2(-3F,-1F);
            }
        }
    
        IEnumerator CancelStop()
        {
            yield return new WaitForSeconds(stopTime);
            state = State.Walk;
        }
    
    
        public void OnTriggerEnter2D(Collider2D collision)
        {
            if (collision.gameObject.tag == "Player" &&
              !PlayerController.instance.isFlash)
            {
                PlayerController.instance.Hurt();
                PlayerInfo.instance.SubHp();
            }
        }
    
        public void ChangeWalkState()
        {
            state = State.Walk;
        }
    
    
        public void Attack()
        {
            GameObject o = Instantiate(puke);
            Vector2 start = this.transform.Find("firePos").position;
            Vector2 end = GameObject.Find("player").transform.position;
            o.transform.position = start;
            Vector2 dir = end - start;
            dir = dir.normalized;
            o.GetComponent<Rigidbody2D>().velocity = dir*pukeSpeed;
            Instantiate(bosspuke);
        }
    
        public void Hit()
        {
            hp--;
            isFlash = true;
            StartCoroutine(ResetFlash());
            if (hp<=0)
            {
                Instantiate(kill);
            }
        }
    
        IEnumerator ResetFlash()
        {
            yield return new WaitForSeconds(flashTime);
            isFlash = false;
        }
    
    }
    
  2. 游戲的大體思路就是這樣,讓我們看看成品的圖片
    pvLuRg.png
    pvLQMj.png
    pvLKzQ.png
    pvLnJS.png
    pvLlss.png
    pvLmi8.png


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM