• 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 角色死亡

     

  • 相关阅读:
    关于做项目
    不一样的Android studio
    你认为一些军事方面的软件系统采用什么样的开发模型比较合适?
    关于Android studio
    面向对象建模所用图的简单总结
    浅谈Android 01
    用例图与类图的联系与区别
    面向过程(或者叫结构化)分析方法与面向对象分析方法到底区别在哪里?请根据自己的理解简明扼要的回答。
    你认为一些军事方面的软件系统采用什么样的开发模型比较合适?
    项目答辩后的感想
  • 原文地址:https://www.cnblogs.com/VastTry/p/8028509.html
Copyright © 2020-2023  润新知