跑酷
一.功能需求.... 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 角色死亡