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