9.开始场景-选角界面
9.1 拼面板
查看选角界面示例图
把canvas预制体拉一个出来,创建选角面板对象,面板中创建各个背景图,选角大字标题文本,金钱文本,角色名字文本,左右切换按钮,开始返回按钮,解锁角色按钮。
9.2 资源准备
在资源文件夹中找到角色模型,拖入对应的武器到预制体专门放武器的子物体下,重新把各个角色打成预制体
给其中一个模型创建Animator文件,关联到对应角色预制体,文件中创建混合树。混合树设置为2D Freeform Directional 2D自由形式定向模式。因为同一方向上可能又有走有有炮。添加横向移动纵向移动两个参数并关联。关联资源中前后左右走和跑加上默认状态9个动画文件,设置参数值。
添加滚动动画和对应的Trigger参数,移动混合树可以直接过渡到滚动动画
做上半身开火动画。首先创建一个人形遮罩,让下半身不收影响。创建动画的开火层。关联刚刚创建的人形遮罩。开火层创建新的空状态不关联任何动画作为一层的默认播放动画,这样就算该层有权重也不会有任何影响
添加开火动画和对应的Trigger参数,开火层空状态可以过渡到开火动画
新建一个层级,作为蹲下时的操作层。可以勾选Sync同步,源层关联基础层。对应的动画就可以重新关联层蹲下时的走路和蹲下时的默认动画。蹲下移动的动画要重新关联,蹲着没有跑的动画,直接设置蹲着走和蹲着默认动画就行。
其他的角色不需要重新创建animator文件,只需要创建动画覆盖控制器,关联第一个角色的animator文件和对应角色的对应动作即可。最后把animator文件关联到其他角色预制体的Animator组件上。其他角色都是按这样的套路进行制作
9.3 数据准备
创建excel表,配置角色的id,资源路径,攻击力,角色名,解锁金币,攻击类型,攻击特效。去网站上转成json。取名RoleInfo.json存储到StreamAssets文件夹下。
[
{"id":1,"res":"Role/1","atk":2,"tips":"最帅的男人","lockMoney":0,"type":1,"hitEff":"eff/1"},
{"id":2,"res":"Role/2","atk":3,"tips":"最温柔的男人","lockMoney":0,"type":2,"hitEff":"eff/1"},
{"id":3,"res":"Role/3","atk":4,"tips":"最凶猛的男人","lockMoney":200,"type":2,"hitEff":"eff/1"},
{"id":4,"res":"Role/4","atk":5,"tips":"最可爱的男人","lockMoney":300,"type":2,"hitEff":"eff/1"},
{"id":5,"res":"Role/5","atk":6,"tips":"最小的男人","lockMoney":400,"type":2,"hitEff":"eff/1"},
{"id":6,"res":"Role/6","atk":7,"tips":"最大的男人","lockMoney":500,"type":2,"hitEff":"eff/1"}
]
创建角色数据脚本RoleInfo,变量名要和配置表一一对应
/// <summary>
/// 角色数据
/// </summary>
public class RoleInfo
{
public int id;
public string res;
public int atk;
public string tips;
public int lockMoney;
public int type;
public string hitEff;
}
创建玩家数据脚本PlayerData,用于记录玩家当前拥有多少钱和解锁了哪些角色
/// <summary>
/// 玩家数据
/// </summary>
public class PlayerData
{
//当前拥有多少游戏币
public int haveMoney = 0;
//当前解锁了哪些角色
public List<int> buyHero = new List<int>();
}
在GameDataMgr脚本中,关联一个PlayerData变量和一个RoleInfo列表。在构造函数中用Json数据管理器初始化读取。提供给外部一个存储玩家数据的方法,用Json数据管理器存储。
/// <summary>
/// 专门用来管理数据的类
/// </summary>
public class GameDataMgr
{
//玩家相关数据
public PlayerData playerData;
//所有的角色数据
public List<RoleInfo> roleInfoList;
private GameDataMgr()
{
//获取初始化玩家数据
playerData = JsonMgr.Instance.LoadData<PlayerData>("PlayerData");
//读取角色数据
roleInfoList = JsonMgr.Instance.LoadData<List<RoleInfo>>("RoleInfo");
}
/// <summary>
/// 存储玩家数据
/// </summary>
public void SavePlayerData()
{
JsonMgr.Instance.SaveData(playerData, "PlayerData");
}
}
9.4 功能制作
创建ChooseHeroPanel脚本,继承BasePanel面板基类,挂载到ChooseHeroPanel选角界面预制体上,注意脚本名要和界面名一样
声明各个UI元素的变量,包括左右键按钮、购买按钮、开始和返回按钮、左上角拥有的钱、角色信息等。名字最好和UI对象名一样,在外部关联。
- 左右键
public Button btnLeft;
public Button btnRight;
- 购买按钮
public Button btnUnLock;
public Text txtUnLock;
- 开始和返回
public Button btnStart;
public Button btnBack;
- 左上角拥有的钱
public Text txtMoney;
- 角色信息
public Text txtName;
把选角界面预制体放到Resources文件夹做成预制体
尝试把角色预制体拖到开始场景摄像机旋转到左边的情况下,看看位置合不合适,或者要不要调整摄像机位置或动画。在角色合适的位置创建空物体heroPos,方便代码到时候生成角色时直接使用该空物体的位置为角色生成的位置。
声明私有变量,包括角色预设体需要创建在的位置、当前场景中显示的对象、当前使用的数据、当前使用数据的索引等
- 英雄预设体需要创建在的位置
private Transform heroPos;
- 当前场景中显示的对象
private GameObject heroObj;
- 当前使用的数据
private RoleInfo nowRoleData;
- 当前使用数据的索引
private int nowIndex;
定义更新选角方法ChangeHero,更新场景上要显示的模型:如果当前场景中已经存在角色模型对象,则销毁该对象。根据当前使用的数据索引从角色数据管理器中获取对应的角色数据,实例化模型对象,并将位置和旋转信息设置为预设体位置。调用更新解锁按钮方法UpdateLockBtn,根据解锁相关数据来决定是否显示解锁按钮
/// <summary>
/// 更新场景上要显示的模型的
/// </summary>
private void ChangeHero()
{
if(heroObj != null)
{
Destroy(heroObj);
heroObj = null;
}
//取出数据的一条 根据索引值
nowRoleData = GameDataMgr.Instance.roleInfoList[nowIndex];
//实例化对象 并记录下来 用于下次切换时 删除
heroObj = Instantiate(Resources.Load<GameObject>(nowRoleData.res), heroPos.position, heroPos.rotation);
//根据解锁相关数据 来决定是否显示解锁按钮
UpdateLockBtn();
}
定义更新解锁按钮方法UpdateLockBtn()。如果当前角色需要解锁且在玩家数据拥有角色列表中还没有对应角色的id,则显示解锁按钮并隐藏开始按钮,更新按钮上的文本为当前角色需要的钱。否则,隐藏解锁按钮并显示开始按钮。
/// <summary>
/// 更新解锁按钮显示情况
/// </summary>
private void UpdateLockBtn()
{
//如果该角色 需要解锁 并且没有解锁的话 就应该显示解锁按钮 并且隐藏开始按钮
if( nowRoleData.lockMoney > 0 && !GameDataMgr.Instance.playerData.buyHero.Contains(nowRoleData.id) )
{
//更新解锁按钮显示 并更新上面的钱
btnUnLock.gameObject.SetActive(true);
txtUnLock.text = "¥" + nowRoleData.lockMoney;
//隐藏开始按钮 因为该角色没有解锁
btnStart.gameObject.SetActive(false);
}
else
{
btnUnLock.gameObject.SetActive(false);
btnStart.gameObject.SetActive(true);
}
}
游戏数据管理器中添加当前选择角色变量,会在选角界面的点击开始按钮后赋值记录当前选择角色
/// <summary>
/// 专门用来管理数据的类
/// </summary>
public class GameDataMgr
{
//记录选择的角色数据 用于之后在游戏场景中创建
public RoleInfo nowSelRole;
}
重写Init()方法,在场景中用 GameObject.Find找到放置对象预设体的位置赋值给当前私有Transform角色位置变量heroPos。从游戏数据管理器中读取玩家数据中有多少钱,更新左上角玩家拥有的钱的文本显示。为左右键按钮添加点击事件监听,点击时更改角色索引(越界的话循环重新设置索引),调用更新角色方法ChangeHero,实现切换角色功能。为购买按钮添加点击事件监听,从游戏数据管理器中读取玩家数据中有多少钱,如果够钱减去当前角色需要解锁的钱,更新界面钱的显示,添加购买角色的id在玩家数据的购买角色表中,用游戏数据管理器保存玩家数据,调用更新解锁按钮方法UpdateLockBtn,如果不够钱显示提示面板提示金钱不足(待补充),实现解锁角色功能。为开始按钮添加点击事件监听,在游戏数据管理器记录当前选择的角色并隐藏选角界面。为返回按钮添加点击事件监听,隐藏选角界面,要让摄像机动画转右后回调显示开始界面。调用更新选角方法ChangeHero更新模型显示。
public override void Init()
{
//找到场景中 放置对象预设体的位置
heroPos = GameObject.Find("HeroPos").transform;
//更新左上角玩家拥有的钱
txtMoney.text = GameDataMgr.Instance.playerData.haveMoney.ToString();
btnLeft.onClick.AddListener(() =>
{
--nowIndex;
if (nowIndex < 0)
nowIndex = GameDataMgr.Instance.roleInfoList.Count - 1;
//模型的更新
ChangeHero();
});
btnRight.onClick.AddListener(() =>
{
++nowIndex;
if (nowIndex >= GameDataMgr.Instance.roleInfoList.Count)
nowIndex = 0;
//模型的更新
ChangeHero();
});
btnUnLock.onClick.AddListener(() =>
{
//点击解锁按钮的逻辑
PlayerData data = GameDataMgr.Instance.playerData;
//当有钱时
if(data.haveMoney >= nowRoleData.lockMoney)
{
//购买逻辑
//减去花费
data.haveMoney -= nowRoleData.lockMoney;
//更新界面显示
txtMoney.text = data.haveMoney.ToString();
//记录购买的id
data.buyHero.Add(nowRoleData.id);
//保存数据
GameDataMgr.Instance.SavePlayerData();
//更新解锁按钮
UpdateLockBtn();
//提示面板 显示购买成功
print("购买成功");
}
else
{
//提示面板 显示 金钱不足
print("金钱不足");
}
});
btnStart.onClick.AddListener(() =>
{
//第一 是记录当前选择的角色
GameDataMgr.Instance.nowSelRole = nowRoleData;
//第二 是隐藏自己 显示场景选择界面
UIManager.Instance.HidePanel<ChooseHeroPanel>();
});
btnBack.onClick.AddListener(() =>
{
UIManager.Instance.HidePanel<ChooseHeroPanel>();
//让摄像机转回去后再显示开始界面
Camera.main.GetComponent<CameraAnimator>().TurnRgiht(() =>
{
UIManager.Instance.ShowPanel<BeginPanel>();
});
});
//更新模型显示
ChangeHero();
}
重写隐藏面板HideMe方法,隐藏面板时如果当前角色模型不为空就立即销毁当前显示的3D模型角色对象
public override void HideMe(UnityAction callBack)
{
base.HideMe(callBack);
//每次隐藏自己时 要把当前显示的3D模型角色 删除掉
if(heroObj != null)
{
DestroyImmediate(heroObj);
heroObj = null;
}
}
在开始界面中的开始按钮点击事件摄像机向左转动画后,添加实际显示选角界面回调的代码
btnStart.onClick.AddListener(() =>
{
//播放摄像机 左转动画 然后 再显示选角面板
Camera.main.GetComponent<CameraAnimator>().
TurnLeft(() =>
{
UIManager.Instance.ShowPanel<ChooseHeroPanel>();
});
//隐藏开始界面
UIManager.Instance.HidePanel<BeginPanel>();
});
9.5 代码
RoleInfo
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 角色数据
/// </summary>
public class RoleInfo
{
public int id;
public string res;
public int atk;
public string tips;
public int lockMoney;
public int type;
public string hitEff;
}
PlayerData
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 玩家数据
/// </summary>
public class PlayerData
{
//当前拥有多少游戏币
public int haveMoney = 0;
//当前解锁了哪些角色
public List<int> buyHero = new List<int>();
}
GameDataMgr
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 专门用来管理数据的类
/// </summary>
public class GameDataMgr
{
private static GameDataMgr instance = new GameDataMgr();
public static GameDataMgr Instance => instance;
//记录选择的角色数据 用于之后在游戏场景中创建
public RoleInfo nowSelRole;
//音效相关数据
public MusicData musicData;
//玩家相关数据
public PlayerData playerData;
//所有的角色数据
public List<RoleInfo> roleInfoList;
private GameDataMgr()
{
//初始化一些默认数据
musicData = JsonMgr.Instance.LoadData<MusicData>("MusicData");
//获取初始化玩家数据
playerData = JsonMgr.Instance.LoadData<PlayerData>("PlayerData");
//读取角色数据
roleInfoList = JsonMgr.Instance.LoadData<List<RoleInfo>>("RoleInfo");
}
/// <summary>
/// 存储音效数据
/// </summary>
public void SaveMusicData()
{
JsonMgr.Instance.SaveData(musicData, "MusicData");
}
/// <summary>
/// 存储玩家数据
/// </summary>
public void SavePlayerData()
{
JsonMgr.Instance.SaveData(playerData, "PlayerData");
}
}
BeginPanel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class BeginPanel : BasePanel
{
public Button btnStart;
public Button btnSetting;
public Button btnAbout;
public Button btnQuit;
public override void Init()
{
btnStart.onClick.AddListener(() =>
{
//播放摄像机 左转动画 然后 再显示选角面板
Camera.main.GetComponent<CameraAnimator>().TurnLeft(() =>
{
UIManager.Instance.ShowPanel<ChooseHeroPanel>();
});
//隐藏开始界面
UIManager.Instance.HidePanel<BeginPanel>();
});
btnSetting.onClick.AddListener(() =>
{
//之后会在这里 显示设置界面
UIManager.Instance.ShowPanel<SettingPanel>();
});
btnAbout.onClick.AddListener(() =>
{
//你可以自己制作一个关于面板 之后在这里显示
});
btnQuit.onClick.AddListener(() =>
{
Application.Quit();
});
}
}
ChooseHeroPanel
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;
public class ChooseHeroPanel : BasePanel
{
//左右键
public Button btnLeft;
public Button btnRight;
//购买按钮
public Button btnUnLock;
public Text txtUnLock;
//开始和返回
public Button btnStart;
public Button btnBack;
//左上角拥有的钱
public Text txtMoney;
//角色信息
public Text txtName;
//英雄预设体需要创建在的位置
private Transform heroPos;
//当前场景中显示的对象
private GameObject heroObj;
//当前使用的数据
private RoleInfo nowRoleData;
//当前使用数据的索引
private int nowIndex;
public override void Init()
{
//找到场景中 放置对象预设体的位置
heroPos = GameObject.Find("HeroPos").transform;
//更新左上角玩家拥有的钱
txtMoney.text = GameDataMgr.Instance.playerData.haveMoney.ToString();
btnLeft.onClick.AddListener(() =>
{
--nowIndex;
if (nowIndex < 0)
nowIndex = GameDataMgr.Instance.roleInfoList.Count - 1;
//模型的更新
ChangeHero();
});
btnRight.onClick.AddListener(() =>
{
++nowIndex;
if (nowIndex >= GameDataMgr.Instance.roleInfoList.Count)
nowIndex = 0;
//模型的更新
ChangeHero();
});
btnUnLock.onClick.AddListener(() =>
{
//点击解锁按钮的逻辑
PlayerData data = GameDataMgr.Instance.playerData;
//当有钱时
if(data.haveMoney >= nowRoleData.lockMoney)
{
//购买逻辑
//减去花费
data.haveMoney -= nowRoleData.lockMoney;
//更新界面显示
txtMoney.text = data.haveMoney.ToString();
//记录购买的id
data.buyHero.Add(nowRoleData.id);
//保存数据
GameDataMgr.Instance.SavePlayerData();
//更新解锁按钮
UpdateLockBtn();
//提示面板 显示购买成功
print("购买成功");
}
else
{
//提示面板 显示 金钱不足
print("金钱不足");
}
});
btnStart.onClick.AddListener(() =>
{
//第一 是记录当前选择的角色
GameDataMgr.Instance.nowSelRole = nowRoleData;
//第二 是隐藏自己 显示场景选择界面
UIManager.Instance.HidePanel<ChooseHeroPanel>();
});
btnBack.onClick.AddListener(() =>
{
UIManager.Instance.HidePanel<ChooseHeroPanel>();
//让摄像机转回去后再显示开始界面
Camera.main.GetComponent<CameraAnimator>().TurnRgiht(() =>
{
UIManager.Instance.ShowPanel<BeginPanel>();
});
});
//更新模型显示
ChangeHero();
}
/// <summary>
/// 更新场景上要显示的模型的
/// </summary>
private void ChangeHero()
{
if(heroObj != null)
{
Destroy(heroObj);
heroObj = null;
}
//取出数据的一条 根据索引值
nowRoleData = GameDataMgr.Instance.roleInfoList[nowIndex];
//实例化对象 并记录下来 用于下次切换时 删除
heroObj = Instantiate(Resources.Load<GameObject>(nowRoleData.res), heroPos.position, heroPos.rotation);
//根据解锁相关数据 来决定是否显示解锁按钮
UpdateLockBtn();
}
/// <summary>
/// 更新解锁按钮显示情况
/// </summary>
private void UpdateLockBtn()
{
//如果该角色 需要解锁 并且没有解锁的话 就应该显示解锁按钮 并且隐藏开始按钮
if( nowRoleData.lockMoney > 0 && !GameDataMgr.Instance.playerData.buyHero.Contains(nowRoleData.id) )
{
//更新解锁按钮显示 并更新上面的钱
btnUnLock.gameObject.SetActive(true);
txtUnLock.text = "¥" + nowRoleData.lockMoney;
//隐藏开始按钮 因为该角色没有解锁
btnStart.gameObject.SetActive(false);
}
else
{
btnUnLock.gameObject.SetActive(false);
btnStart.gameObject.SetActive(true);
}
}
public override void HideMe(UnityAction callBack)
{
base.HideMe(callBack);
//每次隐藏自己时 要把当前显示的3D模型角色 删除掉
if(heroObj != null)
{
DestroyImmediate(heroObj);
heroObj = null;
}
}
}
RoleInfo.json
[
{"id":1,"res":"Role/1","atk":2,"tips":"最帅的男人","lockMoney":0,"type":1,"hitEff":"eff/1"},
{"id":2,"res":"Role/2","atk":3,"tips":"最温柔的男人","lockMoney":0,"type":2,"hitEff":"eff/1"},
{"id":3,"res":"Role/3","atk":4,"tips":"最凶猛的男人","lockMoney":200,"type":2,"hitEff":"eff/1"},
{"id":4,"res":"Role/4","atk":5,"tips":"最可爱的男人","lockMoney":300,"type":2,"hitEff":"eff/1"},
{"id":5,"res":"Role/5","atk":6,"tips":"最小的男人","lockMoney":400,"type":2,"hitEff":"eff/1"},
{"id":6,"res":"Role/6","atk":7,"tips":"最大的男人","lockMoney":500,"type":2,"hitEff":"eff/1"}
]
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com