码云地址gitee.com/zqain/magic…
创建一个2D项目,由图片组成
选择打包方式-安卓
需要配置安卓的SDK
- 官网下载 当前使用的版本
打开,点击下一步
勾选安卓打包支持
再次选择安卓---平台配置
设置视图宽高(屏幕分辨率),竖屏
编辑-首选项
配置JDK SDK路径
搭建所需资源
Res文件夹+Common脚本(高耦合)
创建Canvas画布
这个设置:使屏幕大小进行缩放不变形
制作背景图
添加标题的图片
与上述一样
- 使用空物体包裹title和glow两个图片
粒子系统,星星点点效果
颜色随时间的变化改变
指定层级
粒子特效制定层级
发现看不到粒子
有一处疑惑,就是当我不对粒子进行操作,发现这个粒子不动了。
- Tip:运行之后查看
创建开始界面
创建一个空物体,大小包含整个页面
物体放入
便于在后期隐藏界面
敬语:作为一个开发者,不断提高自己的认知程度
之前使用的按钮点击方式,对于初学者使用这种方式很方便,但是要是使用的规范的写法还是得使用高级一点的才可以!!!!
介绍使用高耦合结构的方式制作按钮的监听事件完成的对应的功能
创建脚本挂载
介绍代码块
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class MainPanel : MonoBehaviour
{
private Button btn_Start;
private Button btn_Shop;
private Button btn_Rank;
private Button btn_Sound;
private void Awake()
{
Init();
}
private void Init()
{
btn_Start = transform.Find("btn_Start").GetComponent<Button>();
btn_Start.onClick.AddListener(OnStartButtonClick);
btn_Shop = transform.Find("btns/btn_Shop").GetComponent<Button>();
btn_Shop.onClick.AddListener(OnShopButtonClick);
btn_Rank = transform.Find("btns/btn_Rank").GetComponent<Button>();
btn_Rank.onClick.AddListener(OnRankButtonClick);
btn_Sound = transform.Find("btns/btn_Sound").GetComponent<Button>();
btn_Sound.onClick.AddListener(OnSoundButtonClick);
}
#region 监听事件点击的方法
/// <summary>
/// 开始按钮的点击
/// </summary>
private static void OnStartButtonClick()
{
}
/// <summary>
/// 商店按钮的点击
/// </summary>
private static void OnShopButtonClick()
{
}
/// <summary>
/// 排行榜按钮的点击
/// </summary>
private static void OnRankButtonClick()
{
}
/// <summary>
/// 音效按钮的点击
/// </summary>
private static void OnSoundButtonClick()
{
}
#endregion
}
游戏界面
新建空物体,开始布置
-
有一点需要注意 当文字的宽度无法显示完全字,勾选Best Fit会自适应宽度,缩放成可以看到的样子
添加button按钮,更改
继续添加一个运行按钮
-
挂载GamePlay脚本到Game物体上
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.Tracing;
using UnityEngine;
using UnityEngine.UI;
public class GamePanel : MonoBehaviour
{
//拿到面板物体
private Button btn_Pause;
private Button btn_Play;
private Text Text_Score;
private Text Text_DiamondCount;
private void Awake()
{
//监听指定的事件码 转到定义EventDefine
EventCenter.AddListener(EventDefine.ShowGamePanel,Show);
Init();
}
private void Init()
{
btn_Pause = transform.Find("btn_Pause").GetComponent<Button>();
//监听事件
btn_Pause.onClick.AddListener(OnPauseButtonClick);
btn_Play = transform.Find("btn_Play").GetComponent<Button>();
btn_Play.onClick.AddListener(OnPlayButtonClick);
Text_Score = transform.Find("Text_Score").GetComponent<Text>();
Text_DiamondCount = transform.Find("Diamond/Text_DiamondCount").GetComponent<Text>();
//隐藏物体
btn_Play.gameObject.SetActive(false);
gameObject.SetActive(false);
}
private void Show()
{
gameObject.SetActive(true);
}
/// <summary>
/// 移除监听
/// </summary>
private void OnDestroy()
{
EventCenter.RemoveListener(EventDefine.ShowGamePanel,Show);
}
/// <summary>
/// 暂停按钮点击
/// </summary>
private void OnPauseButtonClick()
{
btn_Play.gameObject.SetActive(true);
btn_Pause.gameObject.SetActive(false);
//TODO游戏暂停
}
/// <summary>
/// 开始按钮点击
/// </summary>
private void OnPlayButtonClick()
{
btn_Pause.gameObject.SetActive(true);
btn_Play.gameObject.SetActive(false);
//TODO继续游戏
}
}
- 转到定义EventDefine ***右键 添加一个枚举类型
- 在主页脚本中监听事件
随机切换背景
定义集合存放随机的背景
public Sprite[] sprites;这种方法不提倡!!!!
- 创建管理器的容器
创建管理器的容器
新建脚本
//指定头文件 管理其容器
[CreateAssetMenu(menuName ="CreatManagerVarsContainer")]
public class ManagerVars : ScriptableObject
此时可以看到右键新建多了一处我们自己起的名字CreatManagerVarsContainer在脚本同级的文件下创建了一个同名文件
命名为ManagerVarsContainer管理器容器,这是一个.asset结尾的文件
- 添加代码快
public List<Sprite> bgThemeSpriteList = new List<Sprite>(); - 按照bgThemeSpriteList的长度随机背景
BgTheme脚本中增加随机方法
private ManagerVars vars;
private void Awake()
{
vars = new ManagerVars();
m_SpriteRenderer = GetComponent<SpriteRenderer>();
int runValue = Random.Range(0,vars.bgThemeSpriteList.Count);//长度为4的左闭右开的随机值 0-3
m_SpriteRenderer.sprite = vars.bgThemeSpriteList[runValue];
}
- 超出索引范围
使用的
vars = new ManagerVars();问题,拿到的是脚本,不是文件。。每次都是一个新的
Tip:
ManagerVars脚本中添加方法
//获取脚本的方法
public static ManagerVars GetManagerVars()
{
return Resources.Load<ManagerVars>("ManagerVarsContainer");
}
此时的文件ManagerVarsContainer应该放文件夹Resources中
通过静态方法找到文件
BgTheme脚本中vars = ManagerVars.GetManagerVars();
铺垫场景
放入图片
创建层级
-
最高层级Platform 初始位置
ManagerVars脚本中public GameObject normalPlatform; //移动位置
//右边生成就是+nextXPos +nextYPos //左边生成 -nextXPos +nextYPos
public float nextXPos = 0.554f, nextYPos = 0.645f; 在Game文件夹中创建一个平台生成器PlatformSpawner脚本。
PlatformSpawner空物体上挂载PlatformSpawner脚本
将物体设置成预制体,新建文件夹Prefabs
删除面板上的
PlatformSpawner脚本
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformSpawner : MonoBehaviour
{
public Vector3 startSpawnPos;
private int spawnPlatformCount;//生成平台的数量
private ManagerVars vars;
private Vector3 platformSpawnPosition;//生成位置
private bool isLeftSpawn = false;//是否向左生成
// Start is called before the first frame update
void Start()
{
platformSpawnPosition = startSpawnPos;//默认生成位置,初始生成位置
vars = ManagerVars.GetManagerVars();
for (int i = 0; i < 5; i++)
{
spawnPlatformCount = 5;
DecidePath();
}
}
/// <summary>
/// 确定路径
/// </summary>
private void DecidePath()
{
if (spawnPlatformCount > 0)
{
spawnPlatformCount--;
SpawnPlatform();
}
else
{
isLeftSpawn = !isLeftSpawn;//这次向右,下次向左
//平台生成成功
spawnPlatformCount =Random.Range(1,4);
SpawnPlatform();
}
}
/// <summary>
/// 生成平台
/// </summary>
private void SpawnPlatform()
{
GameObject go= Instantiate(vars.normalPlatform,transform);
go.transform.position = platformSpawnPosition;
//更新
if (isLeftSpawn)//向左生成
{
platformSpawnPosition = new Vector3(platformSpawnPosition.x-vars.nextXPos,platformSpawnPosition.y+vars.nextYPos,0);
}
else
{
platformSpawnPosition = new Vector3(platformSpawnPosition.x + vars.nextXPos, platformSpawnPosition.y + vars.nextYPos,0);
}
}
}
放人物
设置层级,让人物在物体之前显示
调整位置
ManagerVars脚本中添加public GameObject characterPre;//人物
PlatformSpawner脚本中添加 //生成人物 GameObject go = Instantiate(vars.characterPre); go.transform.position = new Vector3(0,-1.8f,0);
设置物体碰撞器
玩家跳跃
PlayController脚本挂载到人物预制体身上
使用Dotween插件
此项目只需要前面三个文件
使用的时候 引入
using DG.Tweening;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class PlayController : MonoBehaviour
{
/// <summary>
/// 是否向左移动
/// </summary>
private bool isMoveLeft = false;
private Vector3 nextPlatformLeft, nextPlatformRight;
private ManagerVars vars;
private void Awake()
{
vars = ManagerVars.GetManagerVars();
}
// Update is called once per frame
void Update()
{
//检测到触摸屏幕
if (Input.GetMouseButtonDown(0))
{
Vector3 mousePos = Input.mousePosition;
//点击的是左边屏幕
if (mousePos.x<Screen.width/2)
{
isMoveLeft = true;
}
else if (mousePos.x>Screen.width/2)
{
isMoveLeft = false;
}
Jump();
}
}
/// <summary>
/// 反转
/// 使用Dotween
/// </summary>
private void Jump()
{
if (isMoveLeft)
{
transform.localScale = new Vector3(-1,1,1);
transform.DOMoveX(nextPlatformLeft.x,0.2f);
transform.DOMoveY(nextPlatformLeft.y+0.8f,0.15f);//具有跳跃效果
}
else
{
transform.localScale = Vector3.one;
transform.DOMoveX(nextPlatformRight.x, 0.2f);
transform.DOMoveY(nextPlatformRight.y + 0.8f, 0.15f);//具有跳跃效果
}
}
/// <summary>
/// 碰撞检测
/// </summary>
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Platform")
{
//计算出下一个平台的位置
Vector3 currentPlatformPos = collision.gameObject.transform.position;
nextPlatformLeft = new Vector3(currentPlatformPos.x-vars.nextXPos,currentPlatformPos.y+vars.nextYPos,0);
nextPlatformRight = new Vector3(currentPlatformPos.x + vars.nextXPos, currentPlatformPos.y + vars.nextYPos, 0);
}
}
}
指定平台标签
运行,测试结果:只在原地跳
人物上添加圆形碰撞器
跳着跳着就歪了,将Z轴锁定
解决连续跳跃
定义一个Bool值
private bool isJuming = false;
可以继续跳
点击一次,生成平台的方法
DecidePath
创建游戏管理类,挂载到游戏物体上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public static GameManager Instance;
/// <summary>
/// 是否开始游戏
/// </summary>
public bool IsGameStarted { get; set; }
/// <summary>
/// 游戏是否结束
/// </summary>
public bool IsGameOver { get; set; }
private void Awake()
{
Instance = this;
}
}
摄像机的跟踪
人物身上添加标签Player
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
private Transform target;
private Vector3 offset;
private Vector2 velocity;
private void Update()
{
if (target==null && GameObject.FindGameObjectWithTag("Player")!=null)
{
target = GameObject.FindGameObjectWithTag("Player").transform;//位置
offset = target.position - transform.position;//偏移量
}
}
private void FixedUpdate()
{
if (target != null)
{
//Mathf.SmoothDamp平滑阻尼,游戏中用于做相机的缓冲跟踪和boss直升机跟踪士兵。该函数是Unity3D中Mathf数学运算函数中的一个。
//平滑缓冲,东西不是僵硬的移动而是做减速缓冲运动到指定位置
float posX = Mathf.SmoothDamp(transform.position.x,
target.position.x - offset.x,
ref velocity.x,
0.05f);
float posY = Mathf.SmoothDamp(transform.position.y,
target.position.y - offset.y,
ref velocity.y,
0.05f);
//解决摄像机抖动
if (posY>transform.position.y)
{
transform.position = new Vector3(posX,posY,transform.position.z);
}
}
}
}
制作组合平台预制体
让平台往下落,拼接成 路
给平台添加刚体组件
z轴旋转锁定
选定
不然,平台就直接掉落了
组合平台
将预制体拖到面板
- 复制组件,移除。将此当做父物体
- 新建空物体,将之前复制的组件放入,位置重置
随机平台主题
private Sprite selectPlatformSprite;//选中的平台图
在方法中添加 selectPlatformSprite = vars.PlatformSpriteList[ran];//随机的主题, 还得判断是使用冬季主题,春季主题
类上定义枚举判断
public enum PlatformGroupType { Grass,Winter }
类中定义private PlatformGroupType groupType;//组合平台类型
将选中的平台告知预设体
将新建的脚本挂载到所有的预设体上
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 随机平台主题
/// </summary>
public class PlatformSprite : MonoBehaviour
{
public SpriteRenderer[] spriteRenderers;
public void Init(Sprite sprite)
{
for (int i = 0; i < spriteRenderers.Length; i++)
{
spriteRenderers[i].sprite = sprite;
}
}
}
分别指定Renderer
依次类推
组合平台生成
钉子平台
预制体
制作动画
给他做一个动画,选中Spike按住Ctrl+6制作动画,新建文件夹存放
制作两个 一个左面,一个右面
代码
别忘记指定
反方向障碍物
- 使用的時候出现生成胡位置便宜,使用 的位置方法跟改成LocalPosition
脚本中,添加相反障碍物
面板上指定需要更换方向的(有些是不需要的)
在钉子方向生成平台
private bool spikeSpawnleft=false;//钉子是否存放在左边
private Vector3 spikeDirformPos;//钉子位置
判断钉子的位置
private int afterSpawnSpikeCount;//生成钉子平台之后需要在钉子方向生成的平台数量
private bool isSpawnSpike;//是否生成钉子
在确认路径的时候,先判断是否生成钉子
创建对象池
脚本挂载到空物体
处理方式大致相同,,,设置共有方法
修改为
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 对象池
/// </summary>
public class ObjectPool : MonoBehaviour
{
public static ObjectPool Instance;//设置单例
private List<GameObject> normalPlatformList = new List<GameObject>();
private List<GameObject> commonPlatformList = new List<GameObject>();
private List<GameObject> grassPlatformList = new List<GameObject>();
private List<GameObject> winterPlatformList = new List<GameObject>();
private List<GameObject> spikePlatformLeftList = new List<GameObject>();
private List<GameObject> spikePlatformRightList = new List<GameObject>();
private ManagerVars vars;
/// <summary>
/// 生成5个
/// </summary>
public int initSpawnCount = 5;
private void Awake()
{
Instance = this;
vars = ManagerVars.GetManagerVars();
Init();
}
private void Init()
{
for (int i = 0; i <initSpawnCount ; i++)
{
InstantiateObject(vars.normalPlatformPre,ref normalPlatformList);
}
for (int i = 0; i < initSpawnCount; i++)
{
for (int j = 0; j < vars.commonPlatformGroup.Count; j++)
{
InstantiateObject(vars.commonPlatformGroup[j], ref commonPlatformList);
}
}
for (int i = 0; i < initSpawnCount; i++)
{
for (int j = 0; j < vars.grassPlatformGroup.Count; j++)
{
InstantiateObject(vars.grassPlatformGroup[j], ref grassPlatformList);
}
}
for (int i = 0; i < initSpawnCount; i++)
{
for (int j = 0; j < vars.winterPlatformGroup.Count; j++)
{
InstantiateObject(vars.winterPlatformGroup[j], ref winterPlatformList);
}
}
for (int i = 0; i < initSpawnCount; i++)
{
InstantiateObject(vars.spikePlatformLeft, ref spikePlatformLeftList);
}
for (int i = 0; i < initSpawnCount; i++)
{
InstantiateObject(vars.spikePlatformRight, ref spikePlatformRightList);
}
}
/// <summary>
/// 公有
/// </summary>
private GameObject InstantiateObject(GameObject prefab,ref List<GameObject> addList)
{
GameObject go = Instantiate(prefab,transform);
go.SetActive(false);
addList.Add(go);
return go;
}
/// <summary>
/// 获得普通平台
/// </summary>
public GameObject GetNormalPlatform()
{
for (int i = 0; i < normalPlatformList.Count; i++)
{
if (normalPlatformList[i].activeInHierarchy==false)
{
return normalPlatformList[i];
}
}
return InstantiateObject(vars.normalPlatformPre, ref normalPlatformList);
}
/// <summary>
/// 获得通用
/// </summary>
/// <returns></returns>
public GameObject GetCommonPlatform()
{
for (int i = 0; i < commonPlatformList.Count; i++)
{
if (commonPlatformList[i].activeInHierarchy == false)
{
return commonPlatformList[i];
}
}
int ran = Random.Range(0,vars.commonPlatformGroup.Count);
return InstantiateObject(vars.commonPlatformGroup[ran], ref commonPlatformList);
}
/// <summary>
/// 获得春季
/// </summary>
/// <returns></returns>
public GameObject GetGrassPlatform()
{
for (int i = 0; i < grassPlatformList.Count; i++)
{
if (grassPlatformList[i].activeInHierarchy == false)
{
return grassPlatformList[i];
}
}
int ran = Random.Range(0, vars.grassPlatformGroup.Count);
return InstantiateObject(vars.grassPlatformGroup[ran], ref grassPlatformList);
}
/// <summary>
/// 获得冬季
/// </summary>
/// <returns></returns>
public GameObject GetWinterPlatform()
{
for (int i = 0; i < winterPlatformList.Count; i++)
{
if (winterPlatformList[i].activeInHierarchy == false)
{
return winterPlatformList[i];
}
}
int ran = Random.Range(0, vars.winterPlatformGroup.Count);
return InstantiateObject(vars.winterPlatformGroup[ran], ref winterPlatformList);
}
/// <summary>
/// 获得左边钉子平台
/// </summary>
public GameObject GetLeftSpikePlatform()
{
for (int i = 0; i < spikePlatformLeftList.Count; i++)
{
if (spikePlatformLeftList[i].activeInHierarchy==false)
{
return spikePlatformLeftList[i];
}
}
return InstantiateObject(vars.spikePlatformLeft,ref spikePlatformLeftList);
}
/// <summary>
/// 获得右边钉子平台
/// </summary>
public GameObject GetRightSpikePlatform()
{
for (int i = 0; i < spikePlatformRightList.Count; i++)
{
if (spikePlatformRightList[i].activeInHierarchy == false)
{
return spikePlatformRightList[i];
}
}
return InstantiateObject(vars.spikePlatformRight, ref spikePlatformRightList);
}
}
修改平台中所有关于实例化的代码
依次类推-对于钉子的
玩家死亡 掉落
使用射线检测
- 向下发射一条射线
判断如果当前人物正在向下落
面板设置层级
给平台新制层级
第二种死亡 直接消失
跳的时候检测左右的物体
- 左右新建物体
所有带有障碍物的设置标签,层级
- 测试观看射线长度
看到人物身上发射的射线