本节要点
1.开始界面逻辑
1.手臂不能移动
在控制手臂的脚本中,设置一个 bool 状态值;
然后通过游戏管理器进行状态值的切换控制。
2.飞盘不能生成
其实思路和武器控制是一样的;
但是细节稍有不同。
3.开始游戏按钮
需要创建一个“GameStart”脚本;
挂载到开始 UI 上,使用 OnMouseDown()进行处理。
要在这个鼠标按下事件中改变游戏状态。
2.游戏界面逻辑
1.分数累加
①游戏管理器中新建字段存储分数;
②然后定义一个“增加分数”的公开方法;
③然后在武器射击的时候调用这个方法,增加分数;
④控制 UI 显示分数。
2.倒计时
①游戏管理器中新建字段存储时间;
②定义一个标志位,用于开始和停止倒计时;
③定义一个方法,修改标志位;
④在 Update 中实现倒计时;
⑤控制 UI 显示倒计时。
关键代码
Weapon
public class Weapon : MonoBehaviour
{
//手臂指向鼠标点击的位置
//声明一个手臂位置的变量;
private Transform m_transform;
//利用射线去检测
//声明一个射线的变量 声明一个存贮射线信息存贮的变量
private Ray ray;
private RaycastHit hit;
//定义一个枪口位置的变量;
private Transform m_Point;
//设置特效 线渲染器属性组件 变量
private LineRenderer m_LineRenderer;
//设置特效的声音源 射击声
private AudioSource m_AudioSource;
//为了不让手臂开始就能控制,设置一个bool类型的开关;
private bool canMove = false; //是否控制武器
//定义一个gamemannger的类型 持用脚本进行控制调用它的脚本 单例模式
private GameMannger m_GameMannger;
void Start()
{
//将Transform 位置赋值
m_transform = gameObject.GetComponent<Transform>();
//枪口位置赋值;
// m_Point = gameObject.transform.FindChild("Point").GetComponent<Transform>();
m_Point = m_transform.FindChild("Point");
//特效渲染器组件位置赋值;关键语法 利用枪口的位置设置; 先将查找到的组件 linerenderer 存贮起来
m_LineRenderer = m_Point.gameObject.GetComponent<LineRenderer>();
//将声音源赋值;通过查找武器父类获得下面的射击声音源; 不要勾选paly on awake 和loop;
m_AudioSource = gameObject.GetComponent<AudioSource>();
//对m_GameMannger进行赋值;
m_GameMannger=GameObject.Find("UI").GetComponent<GameMannger>();
}
/// <summary>
/// 改变武器可控制的状态
/// </summary>
public void ChangeCanMove(bool state) {
//直接将参数赋值;用来改变bool类型canMove的值;
canMove = state;
}
void Update()
{
//控制手臂是否可以移动
if (canMove)
{
//使手臂朝向鼠标点击的位置;
//接收射线的变量 利用摄像机获取射线并赋值;
ray = Camera.main.ScreenPointToRay(Input.mousePosition);//从屏幕发射到鼠标点击的位置的射线;
//进行物理检测
if (Physics.Raycast(ray, out hit))
{
//控制手臂的方向
//检测成功后调用 游戏对象位置的 朝向 方法 lookat
m_transform.LookAt(hit.point);//参数是碰撞点
//检查是否有碰撞;
//Debug.Log("Collider");
//场景中没有任何碰撞体 或者打开场景的属性检查;所以手臂不会移动
//因此 要添加网格碰撞器 选中 游戏物体 add component 加组件 mesh Collider;
//但是默认的有4千个顶点,ui设计的只有2千顶点的简模 所以用ui的添加碰撞器
//成功测试后,发现手的旋转关节不对,就是手是手臂的中心点 可以定义一个父类来控制它 不要以为手臂的中心点来控制
//建立一个父类,把之前的代码组件移除 设置好父类坐标,将脚本挂载到父类组件去
//绘制枪口发射线
//与绘制射线方法相同 Debug.DrawRay();
// Debug.DrawLine(m_transform.position,hit.point,Color.red);//参数1: 起始位置 参数2:射线碰撞的位置 参数3:颜色;
//射线出发的位置不对 所以定义一个空物体存贮枪口的位置 transform
//怎么查找父类下的组件呢?
//gameObject.transform.Find("Point");
//如果游戏对象下面有N个叫Point的组件呢;所以我们只要查找父类下的组件名称即可;
// m_Point = gameObject.transform.FindChild("Point").GetComponent<Transform>(); // 这是赋值过程,写到Start方法即可;
//再次绘制射线;
Debug.DrawLine(m_Point.position, hit.point, Color.red);
//绘制特效线的位置;注意关键函数
m_LineRenderer.SetPosition(0, m_Point.position);//参数1:特效起始位置的下标 参数2:特效起始位置下标的位置 (枪口位置)
m_LineRenderer.SetPosition(1, hit.point);//参数1:特效结束位置的下标 参数2:特效结束位置下标的位置 (射线碰撞点)
//射线检测碰撞体后将飞盘击破
//判断条件 碰撞体的标签并且按下鼠标左键
//飞盘射击
if (hit.collider.tag == "FeiPan" && Input.GetMouseButtonDown(0))
{
//如果射击 分数增加
m_GameMannger.AddScore();
//如果射击到,就播放声音;在Start()中已赋值;
m_AudioSource.Play();
//通过碰撞到的子组件查找到父类 Transform
Transform parent = hit.collider.gameObject.GetComponent<Transform>().parent;//注意组件是单数
//通过父类查找到子类下所有的Transform组件;得到的是一个数组
Transform[] feiPans = parent.GetComponentsInChildren<Transform>();//注意组件是复数
//要实现往下掉落,必须动态添加刚体组件
//通过for循环,Transform点出来符物体,然后添加刚体组件
for (int i = 0; i < feiPans.Length; i++)
{
//通过子物体组件寻找出父类添加刚体组件;模拟掉落效果;不用接收赋值;
feiPans[i].gameObject.AddComponent<Rigidbody>();
}
//18个组件掉落后一直掉落并没有销毁,必须把父物体销毁
//2秒后,销毁父物体 通过transform 点出来 父物体组件 销毁的gameobject
GameObject.Destroy(parent.gameObject, 2.0f);
}
}
}
}
}
FeiPanMannger
public class FeiPanMannger : MonoBehaviour {
//定义一个游戏对象用来存贮预置体的;
public GameObject prefab_feiPan;
//定义一个飞盘的Transform组件 用来接收然后吧它设置成父类的组件
private Transform m_Transform;
void Start () {
//协程
//每隔2秒调用一次 注意关键语句 方法名为字符串 注意括号为英文半角格式;
// InvokeRepeating("CreateFeiPan",2.0f,2.0f);
//飞盘不能抑制生成,不符合游戏逻辑 封装方法;
//将m_Transform组件赋值;
m_Transform=gameObject.GetComponent<Transform>();
}
/// <summary>
/// 开始生成飞盘
/// </summary>
public void StartCreateFeiPan() {
InvokeRepeating("CreateFeiPan",0.5f,2.0f);
}
/// <summary>
/// 停止生成飞盘
/// </summary>
public void StopCreateFeiPan() {
//取消所有协程 有2个重载 怎么看重载 左括号输完再按上下左右键
CancelInvoke();
}
/// <summary>
/// 创建生成飞盘;
/// </summary>
void CreateFeiPan() {
//先获取飞盘的位置;
//Vector3 pos = new Vector3(Random.Range(-4.0f,4.0f),Random.Range(0.5f,1.5f),Random.Range(4.0f,13.0f));
////实例化生成飞盘
//GameObject.Instantiate(prefab_feiPan,pos,Quaternion.identity);
//发现以上方法生成太慢;一次生成3个;
for (int i = 0; i < 3; i++)
{
Vector3 pos = new Vector3(Random.Range(-4.0f,4.0f),Random.Range(1.0f,3.5f),Random.Range(-1.0f,13.0f));
//GameObject.Instantiate(prefab_feiPan,pos,Quaternion.identity);
//将实例化的飞盘存贮起来
GameObject go = GameObject.Instantiate(prefab_feiPan, pos, Quaternion.identity) as GameObject;
//将游戏对象下获得Transform组件 设置成父类组件 参数:m_Transform;
//给生成的飞盘设置成一个服物体;
go.GetComponent<Transform>().SetParent(m_Transform);
}
}
void Update () {
}
}
GameMannger
public class GameMannger : MonoBehaviour {
/// <summary>
/// 游戏三种状态
/// </summary>
public enum GameState {
START,
GAME,
END
}
//定义三个游戏对象变量来接收项目中3个游戏UI;
//存贮3个UI界面的引用;
private GameObject m_StratUI;
private GameObject m_GameUI;
private GameObject m_EndUI;
//用来显示分数的界面;
private GUIText m_GUIText_Score;
//用来显示时间界面;
private GUIText m_GUIText_time;
//存贮当前游戏的状态 关键词:GameState;就是枚举的类型 上面定义好的;
private GameState gameState;
//背景音乐管理 控制音乐只在游戏进行中播放,其他状态不播放;
private AudioSource m_BG_AudioSource;
//武器的脚本 持有武器的脚本才能在游戏管理器里面控制武器的可动性
private Weapon m_Weapon;
//飞盘脚本 持用飞盘脚本控制生成状态;游戏开始界面和结束界面不生成;
private FeiPanMannger m_FeiPanMannger;
//定义一个分数的变量用来存贮分数用的;
private int score=0;
//定义一个时间的变量用来计时的;
private float time = 20;
//定义一个时间的标志位(开关);
private bool startTime = false;
void Start () {
//赋值过程 通过Find方法去查找并赋值;
//通过查找持有三个UI的引用
m_StratUI = GameObject.Find("StratUI");
m_GameUI = GameObject.Find("GameUI");
m_EndUI = GameObject.Find("EndUI");
//用来显示分数;通过查找持有分数UI的引用;
m_GUIText_Score = GameObject.Find("GameScore").GetComponent<GUIText>();
//用来显示时间,通过查找持用时间UI的应用;
m_GUIText_time = GameObject.Find("GameTime").GetComponent<GUIText>();
//将背景音乐赋值 通过查找持有背景音乐的引用
// m_BG_AudioSource = GameObject.Find("ShooterWeapon") as AudioSource; 只能查找组件,不能直查找组件里的内容;
m_BG_AudioSource=GameObject.Find("Main Camera").GetComponent<AudioSource>();
//只保留游戏开始界面,其他两个界面隐藏;
//m_GameUI.SetActive(false);
//m_EndUI.SetActive(false);
//对武器脚本进行赋值; 一定要在游戏状态改变之前先赋值;
m_Weapon = GameObject.Find("Weapon").GetComponent<Weapon>();
//对飞盘脚本进行赋值;
m_FeiPanMannger=GameObject.Find("FeiPanParent").GetComponent<FeiPanMannger>();
//传递游戏状态
ChangeGameState(GameState.START);//开始
//ChangeGameState(GameState.GAME);//游戏进行中
// ChangeGameState(GameState.END);//结束;
//score=GameObject.Find("UI").GetComponent<>();
}
/// <summary>
/// 增加分数的方法
/// </summary>
public void AddScore() {
//增加分数
score++;
//显示分数;
m_GUIText_Score.text ="分数:"+ score+"分";
}
/// <summary>
/// 开启倒计时
/// </summary>
public void StartTime() {
startTime = true;
//在UpDate中修改时间
}
/// <summary>
/// 改变游戏的状态
/// </summary>
/// <param name="state"> 当前状态</param>
public void ChangeGameState(GameState state){
//存贮传过来的游戏状态
gameState=state;
//开始判断转过来的游戏状态;枚举类型
if (gameState==GameState.START) //游戏开始
{
//游戏开始时 游戏界面可用 其他两个界面隐藏;
m_StratUI.SetActive(true);
m_GameUI.SetActive(false);
m_EndUI.SetActive(false);
//游戏开始界面不播放
m_BG_AudioSource.Stop();
//控制武器脚本里的方法 设置相关的值;
m_Weapon.ChangeCanMove(false);
//调用控制飞盘的生成状态脚本的方法;
// m_FeiPanMannger.StopCreateFeiPan(); 起始不用写,写了也没意义
}else if (gameState==GameState.GAME) //游戏进行中
{
m_StratUI.SetActive(false);
m_GameUI.SetActive(true);
m_EndUI.SetActive(false);
//游戏进行中播放音乐
m_BG_AudioSource.Play();
//控制武器脚本里的方法 设置相关的值;
m_Weapon.ChangeCanMove(true);
//调用控制飞盘的生成状态脚本的方法;
m_FeiPanMannger.StartCreateFeiPan();
//开始倒计时:调用方法
StartTime();
}else if (gameState==GameState.END) //游戏结束
{
m_StratUI.SetActive(false);
m_GameUI.SetActive(false);
m_EndUI.SetActive(true);
//游戏结束状态也不播放
m_BG_AudioSource.Stop();
//控制武器脚本里的方法 设置相关的值;
m_Weapon.ChangeCanMove(false);
//调用控制飞盘生成状态脚本的方法
m_FeiPanMannger.StopCreateFeiPan();
}
}
void Update () {
//修改倒计时
if (startTime)//为真,开始倒计时;
{
time -= Time.deltaTime;
//显示时间
m_GUIText_time.text = "时间:" + time + "秒";
}
}
}
GameStart
public class GameStart : MonoBehaviour {
//按下鼠标后必须改变有的状态 所以必须持有gamemannger的脚本
private GameMannger m_GameMannger;
void Start () {
//赋值
m_GameMannger=GameObject.Find("UI").GetComponent<GameMannger>();
}
/// <summary>
/// 按下鼠标键游戏开始 脚本是挂在“游戏开始”这个UI上
/// </summary>
void OnMouseDown() {
// Debug.Log("游戏开始");
//改变游戏的状态;调用这个脚本下的方法的类型的枚举
m_GameMannger.ChangeGameState(GameMannger.GameState.GAME);//脚本的名称下的枚举类型
}
void Update () {
}
}