Unity3D 實現方塊跑酷


                                          跑酷

一.功能需求.... 1

1.1 UI 界面自適應.... 1

1.2地圖生成算法.... 1

1.3.角色控制.... 1

1.4.角色與地圖交互.... 1

1.5 Android 打包.... 2

1.6.其他技術.... 2

二.UI界面自適應.... 2

2.1 自己添加一個分辨率 1080X1920. 2

2.2 固定分辨率.... 3

三.地圖生成算法.... 3

3.1>地圖基本生成(地面加牆壁).... 3

3.2>地圖數據存儲(List+Array).... 3

3.3>連續生成(判斷角色位置)... 8

3.4>地面塌陷(協程定時銷毀)... 9

3.5>路障生成(隨機算法+協程實現動畫).... 10

四.角色的控制.... 12

4.1 實現的思路.... 12

4.2實現的代碼.... 12

五.角色與地圖交互.... 13

5.1>蝸牛痕跡(渲染地面模型)... 13

5.2>死亡判斷(List+觸發)... 15

5.3>得分判斷(List 位移+觸發).... 16

六.Android 打包.... 17

1>JDK 和 SDK 配置.... 17

2>APK 打包細節設置.... 18

3>觸屏操作控制.... 20

七.其他技術.... 21

1>游戲邏輯重置.... 21

2>PlayerPrefs 存儲數據.... 22

3>攝像機跟隨.... 22

八.部分效果展示.... 24

8.1 游戲開始.... 24

8.2游戲中.... 24

8.3 角色死亡.... 25

 

 

一.功能需求

1.1 UI 界面自適應

該案例自適應所有 16:9 分辨率的手機

1.2地圖生成算法

1>地圖基本生成(地面加牆壁)

2>地圖數據存儲(List+Array)

3>連續生成(判斷角色位置)

4>地面塌陷(協程定時銷毀)

5>路障生成(隨機算法+協程實現動畫)

1.3.角色控制

1>基本行走(List+Array)

2>邊界控制(List+Array)※

1.4.角色與地圖交互

1>蝸牛痕跡(渲染地面模型)

2>死亡判斷(List+觸發)

3>得分判斷(List 位移+觸發)

1.5 Android 打包

1>JDK 和 SDK 配置

2>APK 打包細節設置

3>觸屏操作控制

1.6.其他技術

1>游戲邏輯重置

2>PlayerPrefs 存儲數據

3>攝像機跟隨

 

 

 

二.UI界面自適應

2.1 自己添加一個分辨率 1080X1920

 

 

2.2 固定分辨率

 

 

三.地圖生成算法

3.1>地圖基本生成(地面加牆壁)

3.2>地圖數據存儲(List+Array)

單排地圖

 

 

雙排地圖

 

 

public void CreateMapItem(int x(定義的偏移量))

    {

       

     

 

        ///單排地圖

        for (int i = 0; i < 10; i++)//生成的行數即3維坐標(x,y,z)Z的值

        {

            GameObject[] tiles1 = new GameObject[6];//定義一個存放游戲對象的數組並初始化長度為6. 用於存放單排的瓷磚和牆

            for (int j = 0; j <6; j++)//生成的列數即3維坐標(x,y,z)X的值.

            {

                Vector3 pos = new Vector3(j * lenth, 0, i * lenth + x * lenth);//定義每塊瓷磚的生成位子

                Vector3 rot = new Vector3(-90, 45, 0);//定義每塊瓷磚的旋轉角度

             

                Color dp = new Color(134 / 255f, 113 / 255f, 173 / 255f); //定義每塊瓷磚的顏色

     

                 if (j == 0 || j == 5)//每一列的最開始和最后的一個生成牆壁

                 {

                                     tile = Instantiate(pre_wall2, pos, Quaternion.Euler(rot)); //生成牆

                      tile.GetComponent<Transform>().SetParent(m_tf);//把每塊牆設置為Mapmanager的子物體

                      tile.GetComponent<MeshRenderer>().material.color = new Color(115 / 255f, 86 / 255f, 139 / 255f);// 改變每塊牆的顏色

                    

                    

                 }

                 else

                 {

                     int pr = CalcPR(); //定義一個值獲取概率函數產生的值即(0-3)

                     switch (pr)

                     {

                                   case 0:

                                          tile = Instantiate (pre_tile, pos, Quaternion.Euler (rot));

                                          tile.GetComponent<Transform> ().SetParent (m_tf);

                                          tile.GetComponent<Transform> ().Find ("normal_a2").gameObject.GetComponent<MeshRenderer> ().material.color = dp;

                                          Transform tile_transform = tile.GetComponent<Transform> ();

                                          int pre_gem = CalcGamePR ();  //在產生瓷磚的前提下,如果 pre_gem=1 則在瓷磚上方產生金幣。

                                          if (pre_gem == 1) {

                                                 GameObject gem=      Instantiate(pre_Gem,tile_transform.position+new Vector3(0.0f,0.06f,0.0f),Quaternion.identity);

                                                 gem.GetComponent<Transform>().SetParent (tile_transform); //把金幣設置為瓷磚的子物體,實現沒被吃掉的金幣同瓷磚一起崩塌。

                                          }

                            break;

                          case 1:

                               GameObject  tile = new GameObject();

                            tile.GetComponent<Transform>().position = pos;//設置瓷磚的位子

                            tile.GetComponent<Transform>().SetParent(m_tf);//設置瓷磚的父物體

                            break;

                          case 2:

                            tile = Instantiate(pre_moving_spikes, pos, Quaternion.Euler(rot));

                            tile.GetComponent<Transform>().SetParent(m_tf);

 

 

                            break;///地面陷阱

                          case 3:

                            tile = Instantiate(pre_smashing_spikes, pos, Quaternion.Euler(rot));

                            tile.GetComponent<Transform>().SetParent(m_tf);

 

                            break;///天空陷阱

 

                     }

                  

                 }

 

                 tiles1[j] = tile; //存放每一行的瓷磚和牆壁到數組中

               

            }

            list.Add(tiles1); //把10行單排的地圖存放在List中

            ///雙排地圖

            GameObject[] tiles2 = new GameObject[5]; 定義一個存放游戲對象的數組並初始化長度為5 用於存放雙排的瓷磚和牆

            for (int j = 0; j < 5; j++) //生產雙排瓷磚的列數

            {

                Vector3 pos1 = new Vector3(lenth / 2.0f + j * lenth, 0, i * lenth + lenth / 2.0f + x * lenth);

                Vector3 rot = new Vector3(-90, 45, 0);

                Color dp = new Color(146 / 255f, 120 / 255f, 180 / 255f);

                 int pr = CalcPR();

                    

             

                switch (pr)

                {

                    case 0:

                                    tile = Instantiate(pre_tile, pos1, Quaternion.Euler(rot));

                        tile.GetComponent<Transform>().SetParent(m_tf);

                        tile.GetComponent<Transform>().Find("normal_a2").gameObject.GetComponent<MeshRenderer>().material.color = dp;

                                   Transform tile_transform = tile.GetComponent<Transform> ();

                                   int pre_gem = CalcGamePR ();//同上的生成金幣

                                   if (pre_gem == 1) {

                                          GameObject gem=      Instantiate(pre_Gem,tile_transform.position+new Vector3(0.0f,0.06f,0.0f),Quaternion.identity);

                                          gem.GetComponent<Transform> ().SetParent (tile_transform);

                                   }

 

                                   break;

                            case 1:

                        tile = new GameObject();

                        tile.GetComponent<Transform>().position = pos1;

                        tile.GetComponent<Transform>().SetParent(m_tf);

                        break;

                    case 2:

                        tile = Instantiate(pre_moving_spikes, pos1, Quaternion.Euler(rot));

                        tile.GetComponent<Transform>().SetParent(m_tf);

                      

 

                        break;///地面陷阱

                    case 3:

                         tile = Instantiate(pre_smashing_spikes, pos1, Quaternion.Euler(rot));

                        tile.GetComponent<Transform>().SetParent(m_tf);

 

                        break;///天空陷阱

                }

                tiles2[j] = tile; 

            }

          

            list.Add(tiles2);

        }

  

 

3.3>連續生成(判斷角色位置)

3.3.1 實現的思路

1.當Player移動時到達指定的行數(高度)即Z值等於指定的數值(List長度減去一定的數值)時實現地圖的連續生成.
2.第一張地圖是游戲開始就生成的,第2張地圖的生成點如圖容易知道為x+10*lenth(對角線長度的一半),第3張則為x+20*lenth以此類推。

 

 

3.新地圖主要是Z的值在改變,x和y的值不變

Vector3 pos = new Vector3(j * lenth, 0, i * lenth + x * lenth)

 

3.3.2.主要實現的代碼

public void UpdateMap()

    {

        if (z == m_MapManager.list.Count - 10) //當z等於List(每一個地圖的list=20)長度減去10時,即z在每個地圖的中間時開始生成下一個地圖

        {

            m_MapManager.CreateMapItem((j++) * 10);//j一開始等於1,調用生成地圖的函數實現生成下一個地圖。

            m_MapManager.AddPR();//每生成一個地圖相應陷阱的生產概率也隨之增加。

        }

 

    }

 

3.4>地面塌陷(協程定時銷毀)

3.4.1 實現的思路

1.循環遍歷list 為每個游戲對象添加鋼體
2.當崩塌趕上了Player后停止崩塌

3.4.2 實現的主要代碼

private IEnumerator TileDown()

    {

 

      

        for (int i = 0; i < list.Count; i++)

        {

                     if (i == m_player.z) //崩塌趕上了角色停止崩塌

           {

               StopTileDown();

                            if (m_player.life) {

                                   m_player.gameObject.AddComponent<Rigidbody>();

                                   m_player.StartCoroutine ("gameover");

                            }              

          }

            for (int j = 0; j < list[i].Length; j++)

            {

                Rigidbody rib= list[i][j].AddComponent<Rigidbody>();

           rib.angularVelocity = new Vector3(Random.Range(-1.0f, 1.0f), Random.Range(-1.0f, 1.0f), Random.Range(-1.0f, 1.0f)) * 3.0f;

                Destroy(list[i][j], 2);

            }

            yield return new WaitForSeconds(0.3f);//0.3s后實現下一行的崩塌

 

         

}

    }

 

3.5>路障生成(隨機算法+協程實現動畫)

3.5.1 隨機算法

  private int CalcPR() //路障的隨機算法

    {

        int rd = Random.Range(1, 101);//定義一個隨機范圍(1-100)

        if (rd <=pr_hole)//坑

        {return 1;}

        else if (rd >31&& rd<=31+ms) //地面刺

        {return 2;}

        else if(rd >61&&rd<=61+ss)//天空陷阱

        {return  3;}

              else 

        return 0;

    }

 

    /// <summary>

    /// 增加概率

    /// </summary>

    public void AddPR()//路障產生的概率增加

    {

        pr_hole+=2;

        ms+=1;

        ss+=1;

    }

       private int CalcGamePR(){ //金幣產生的隨機算法

 

              int rd = Random.Range (1, 101);

              if (rd <= goal) {

                     return 1;

              } else

                     return 0;

}

3.5.2 協程實現

    private IEnumerator Up()//實現地面的陷阱的刺向上刺

    {

 

        while (true)

        {

            m_sontransform.position = Vector3.Lerp(m_sontransform.position, targetPosition, Time.deltaTime*3.0f);

            yield return null;

        }

 

    }

    private IEnumerator Down()//實現地面的陷阱的刺向下

 

    {

        while (true)

        {

 

            m_sontransform.position = Vector3.Lerp(m_sontransform.position, nomalPosition, Time.deltaTime*3.0f);

            yield return null;

        }

 

    }

    private IEnumerator UpAndDown()

    {

 

        while (true)

        {

            StartCoroutine("Up"); //開始向上刺

            yield return new WaitForSeconds(6);//6秒后

            StopCoroutine("Up");//停止向上移動

            StartCoroutine("Down");//開始向下移動

            yield return new WaitForSeconds(6);//停止6s后

            StopCoroutine("Down");//停止向下移動

//目的是為了向上移動的時候只向上移動,向下移動的時候只向下移動。

        }

 

}

 

 

四.角色的控制

4.1 實現的思路

1.向左邊移動,當z為奇數且x>0的時候 x才會減一,並且x不等於0時z才可以繼續移動

2.向右移動時 如果z不是奇數時且x不等於4 Z才可以改變,如果Z時偶數且x小於4 x的值才可以發生改變

4.2實現的代碼

public void PlayerMove()

    {

        if (Input.GetKeyDown(KeyCode.A))

        {

            Left();

        }

        if (Input.GetKeyDown(KeyCode.D))

        {

            Right();

        }

    }

    public void Left()

    {

        if (life)//向左移動

        {

            if (x != 0) { z++; }

            if (z % 2 == 1 && x > 0)

            {

 

                x = x - 1;

 

            }

 

 

 

            Setposition();

 

        }

       

    }

    public void Right()//向右移動

    {

        if (life)

        {

            if (!(z % 2 == 1 && x == 4)) { z++; }

            if (z % 2 == 0 && x < 4)

            {

 

                x = x + 1;

            }

 

            Setposition();

        }

       

        

}

 

 

 

 

五.角色與地圖交互

5.1>蝸牛痕跡(渲染地面模型)

5.1.1實現的思路

1.通過獲取list[z][x]來獲取該物體並獲取其Meshrender組件的material下的color屬性實現對Player走過的路徑奇偶行的顏色不一樣。

2.實現的代碼

  public void Setposition()

    {

        Transform tileTransform = m_MapManager.GetList()[z][x].GetComponent<Transform>();

         m_transform.position = tileTransform.position + new Vector3(0, m_MapManager.Lenth / 2.0f, 0);

        m_transform.rotation = tileTransform.rotation;

 

 

        if (z % 2 == 1)

        {

            if (tileTransform.tag == "tile")

            { tileTransform.Find("normal_a2").gameObject.GetComponent<MeshRenderer>().material.color = new Color(0.5f, 0.5f, 0.5f); }

            else if (tileTransform.tag == "moving_spikes")

            { tileTransform.Find("moving_spikes_a2").gameObject.GetComponent<MeshRenderer>().material.color = new Color(0.5f, 0.5f, 0.5f); }

            else if (tileTransform.tag == "smashing_spikes")

            { tileTransform.Find("smashing_spikes_a2").gameObject.GetComponent<MeshRenderer>().material.color = new Color(0.5f, 0.5f, 0.5f); }

 

            else

            {

                gameObject.AddComponent<Rigidbody>();

                StartCoroutine("gameover");

            }

        }

        else

        {

 

            if (tileTransform.tag == "tile")

            { tileTransform.Find("normal_a2").gameObject.GetComponent<MeshRenderer>().material.color = new Color(0.9f, 0.9f, 0.9f); }

            else if (tileTransform.tag == "moving_spikes")

            { tileTransform.Find("moving_spikes_a2").gameObject.GetComponent<MeshRenderer>().material.color = new Color(0.9f, 0.9f, 0.9f); }

            else if (tileTransform.tag == "smashing_spikes")

            { tileTransform.Find("smashing_spikes_a2").gameObject.GetComponent<MeshRenderer>().material.color = new Color(0.9f, 0.9f, 0.9f); }

 

            else

            {

                gameObject.AddComponent<Rigidbody>();

                StartCoroutine("gameover");

            }

 

 

 

        }

        UpdateMap();

              AddScoreCount ();

    }

5.2>死亡判斷(List+觸發)

1.被天空刺刺到

調用

  private IEnumerator gameover()

    {

        

                 life = false;

            Music.PlayOneShot(GameOver);

            bg_Music.Stop();

                    

                     SaveData ();

            yield return null;

            StartCoroutine("GameReset");  

    }

 

2.被地面陷阱刺到

同上

3.掉到坑里

{

                gameObject.AddComponent<Rigidbody>();

                StartCoroutine("gameover");

            }

4.被崩塌追趕到

if (i == m_player.z)

           {

               StopTileDown();

                            if (m_player.life) {

                                   m_player.gameObject.AddComponent<Rigidbody>();

                                   m_player.StartCoroutine ("gameover")

}    

5.3>得分判斷(List 位移+觸發)

1.每走一步分數加一

       private void AddScoreCount(){

      

              score_count++;

              //print ("Score_count:" + score_count);

        m_game.UpdateGame(gem_score, score_count);

       }

2.吃一金幣,金幣數加一

       private void AddGemCount(){

              gem_score++;

              //print ("gem_score:" + gem_score);

        m_game.UpdateGame(gem_score, score_count)

       }

 

 

六.Android 打包

1>JDK 和 SDK 配置

 

 

 

 

2>APK 打包細節設置

 

 

 

3>觸屏操作控制

1.添加一個透明度為0的圖片並附加Ui button (左半邊屏幕)

 

 

 

2.添加點擊事件

 

 

3.添加一個透明度為0的圖片並附加Ui button (右半邊屏幕)

 

 

 

4.添加點擊事件

 

 

 

 

 

七.其他技術

1>游戲邏輯重置

    private IEnumerator GameReset()

    {

        yield return new WaitForSeconds(2);

        //角色重置

        PLayerReset();

        //地圖重置

        m_MapManager.MapReset();

        //UI重置

        m_game.UIReset();

        //攝像機重置

        m_ca.CameraReset();

     

        yield return null;

    }

2>PlayerPrefs 存儲數據

private void SaveData(){

      

              PlayerPrefs.SetInt ("gem", gem_score);//存儲金幣數到注冊表

              if (score_count > PlayerPrefs.GetInt ("score", 0))

                     PlayerPrefs.SetInt ("score", score_count);//當移動分數大於注冊表的數值實現更新,即存儲最高分

       }

 

3>攝像機跟隨

using System.Collections;

using System.Collections.Generic;

using UnityEngine;

 

public class Camerafollow : MonoBehaviour {

    private Transform m_player;

    private Transform m_transform;

    private Vector3 m_nextPos;

    private bool startFollow = false;

    private Vector3 m_nomal;

       // Use this for initialization

    void Start()

    {

    

        m_transform = gameObject.GetComponent<Transform>();

        m_player=GameObject.Find("cube_books").GetComponent<Transform>();

        m_nextPos=new Vector3(m_transform.position.x,m_transform.position.y,m_player.position.z);

        m_nomal = m_transform.position;

    }     

       // Update is called once per frame

       void Update () {

        if (startFollow)

        {

            Follow();

        }

       

       }

    public void SetStartFollow(bool startfollow)

    {

        startFollow = startfollow;

    }

   

    /// <summary>

    /// 啟動攝像機跟隨

    /// </summary>

    public void Follow()

    {

     

            m_nextPos = new Vector3(m_transform.position.x, m_transform.position.y, m_player.position.z);//只改變z的值

            m_transform.position = Vector3.Lerp(m_transform.position, m_nextPos, Time.deltaTime);//平滑的過度

       

 

    }

    public void CameraReset()

    {

     

        m_transform.position = m_nomal;

        SetStartFollow(false);

       

    }

}

 

八.部分效果展示

8.1 游戲開始

 

 

8.2游戲中

 

 

 

8.3 角色死亡

 

 


免責聲明!

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



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