Unity安卓2D幻径游戏

909 阅读7分钟

码云地址gitee.com/zqain/magic… 创建一个2D项目,由图片组成 选择打包方式-安卓 需要配置安卓的SDK

  • 官网下载 当前使用的版本 打开,点击下一步 勾选安卓打包支持 再次选择安卓---平台配置

设置视图宽高(屏幕分辨率),竖屏

编辑-首选项 配置JDK SDK路径

进入查看juejin.cn/post/687264…

搭建所需资源

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);
    }
}

修改平台中所有关于实例化的代码 依次类推-对于钉子的

玩家死亡 掉落

使用射线检测

  • 向下发射一条射线 判断如果当前人物正在向下落 面板设置层级 给平台新制层级

第二种死亡 直接消失

跳的时候检测左右的物体

  • 左右新建物体 所有带有障碍物的设置标签,层级
  • 测试观看射线长度 看到人物身上发射的射线

点击跳转下文