当前位置:   article > 正文

Unity3D第一人称射箭游戏作业

Unity3D第一人称射箭游戏作业

游戏要求

开发一个第一人称射箭游戏,应满足以下要求:

  •  基础分(2分):有博客;
  •  1-3分钟视频(2分):视频呈现游戏主要游玩过程;
  •  地形(2分):使用地形组件,上面有草、树;
  •  天空盒(2分):使用天空盒,天空可随玩家位置 或 时间变化 或 按特定按键切换天空盒;
  •  固定靶(2分):有一个以上固定的靶标;
  •  运动靶(2分):有一个以上运动靶标,运动轨迹,速度使用动画控制;
  •  射击位(2分):地图上应标记若干射击位,仅在射击位附近可以拉弓射击,每个位置有 n 次机会;
  •  驽弓动画(2分):支持蓄力半拉弓,然后 hold,择机 shoot;
  •  游走(2分):玩家的驽弓可在地图上游走,不能碰上树和靶标等障碍;
  •  碰撞与计分(2分):在射击位,射中靶标的相应分数,规则自定;

具体设计为:

-设置静止靶单倍区和运动靶双倍区两个区域可以射击,其他区域没有靶子无法射击得分。

-靶子分为白、黑、红自外向里为3、4、5分,集中运动靶分数则翻倍

-一共有10支箭矢,全部用完则游戏结束

-长按空格可以为弓弩蓄力,再次按下空格则发射

游戏效果展示

视频展示:

主要功能代码实现:

游戏总控:MainController

负责游戏一开始的初始化,和游戏进行中将信息传递到不同的脚本中,如为视图View传递分数、当前位置等信息。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. /* 游戏状态,0为准备进行,1为正在进行游戏,2为结束 */
  5. enum GameState {
  6. Ready = 0, Playing = 1, GameOver = 2
  7. };
  8. public class MainController : MonoBehaviour
  9. { private View view; // 游戏视图
  10. private CrossController crossController;//十字弩控制器
  11. private int gameState;//游戏状态
  12. public GUISkin gameSkin; //按键皮肤
  13. public Material[] skyboxMaterials;//存储天空盒数组
  14. private int index;//天空盒数组下标
  15. private int lessnum;//剩余箭矢数
  16. public GameObject cross; //十字弩
  17. // Start is called before the first frame update
  18. void Start()//start中进行初始化
  19. {Director.GetInstance().mainController = this;
  20. crossController= gameObject.AddComponent<CrossController>();
  21. gameState = (int)GameState.Ready;
  22. view = gameObject.AddComponent<View>();
  23. view.gameSkin = gameSkin;
  24. // 隐藏十字弩
  25. cross.SetActive(false);
  26. //携程切换天空盒
  27. index=-1;
  28. StartCoroutine(ChangeSkybox());
  29. }
  30. public void SetGameState(int state) {//设置游戏状态
  31. gameState = state;
  32. }
  33. public void Restart() {//重启游戏函数
  34. view.Init();
  35. lessnum=10;
  36. roundController.Reset();
  37. CrossC crossComponent = cross.GetComponent<CrossC>();
  38. crossComponent.Init();
  39. }
  40. public void ShowPage() {//控制视图view的页面
  41. switch(gameState) {
  42. case 0:
  43. view.ShowHomePage();
  44. break;
  45. case 1:
  46. view.ShowGamePage();
  47. break;
  48. case 2:
  49. view.ShowRestart();
  50. break;
  51. }
  52. }
  53. public void SetViewScore(int score) {//将十字弩的计分传给视图
  54. view.SetScore(score);
  55. }
  56. public void SetViewNum(int num) {//将十字弩的剩余箭矢数传给视图
  57. lessnum=num;
  58. view.SetNum(num);
  59. }
  60. public void SetViewX(float x){//将十字弩的位置传给视图
  61. view.GetX(x);
  62. }
  63. public void Update(){//实时传递游戏状态给视图
  64. CrossC crossComponent = cross.GetComponent<CrossC>();
  65. SetViewScore(crossComponent.getScore());
  66. SetViewNum(crossComponent.getNum());
  67. SetViewX(crossComponent.getX());
  68. if(lessnum==0) SetGameState(2);//剩余箭为0时结束游戏
  69. }
  70. IEnumerator ChangeSkybox()
  71. {
  72. while (true)
  73. {
  74. // 循环选择一个天空盒子材质
  75. index = (index+1)%3;
  76. RenderSettings.skybox = skyboxMaterials[index];
  77. // 等待一段时间后再切换天空盒子
  78. yield return new WaitForSeconds(20);
  79. }
  80. }
  81. }

视图控制:View

负责控制游戏的页面提示,向玩家展示游戏的进行状态

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. public class View : MonoBehaviour
  6. {
  7. private MainController mainController;
  8. private int score;//当前得分
  9. private int arrownum;//当前剩余箭矢数
  10. private float x;//当前位置
  11. public GUISkin gameSkin;
  12. // Start is called before the first frame update
  13. void Start()//初始化
  14. { score = 0;
  15. mainController = Director.GetInstance().mainController;
  16. }
  17. // 分别获取分数、剩余箭矢数和位置的函数
  18. public void SetScore(int score) {
  19. this.score = this.score+score;
  20. }
  21. public void SetNum(int num){
  22. arrownum=num;
  23. }
  24. public void GetX(float a){
  25. x=a;
  26. }
  27. public void Init(){//开始游戏的初始化
  28. score=0;
  29. arrownum=10;
  30. }
  31. public void ShowHomePage() {
  32. AddStartButton();
  33. }
  34. public void ShowGamePage(){
  35. AddGameLabel();
  36. }
  37. public void ShowRestart() {
  38. AddRestartlabel();
  39. }
  40. public void AddStartButton(){//GameState=0时,展示初始界面
  41. GUI.skin = gameSkin;
  42. if(GUI.Button(new Rect(340,200,160,80),"点击开始")){
  43. mainController.Restart();
  44. mainController.SetGameState((int)GameState.Playing);//按下开始按钮则进入游戏状态
  45. }
  46. }
  47. public void AddGameLabel() {//GameState=1时,展示游戏界面
  48. GUIStyle labelStyle = new GUIStyle();
  49. labelStyle.normal.textColor = Color.black;
  50. labelStyle.fontSize = 30;
  51. mainController.cross.SetActive(true);
  52. mainController.cross.transform.rotation=Quaternion.Euler(-10f, 0f, 0f);
  53. GUI.Label(new Rect(570, 10, 100, 50), "得分: " + score, labelStyle);//展示得分、剩余箭矢数及提示当前区域
  54. GUI.Label(new Rect(570, 45, 100, 50), "剩余箭数: " + arrownum, labelStyle);
  55. if(x<-9.96||(x>10.05&&x<19.00)||x>40.00) GUI.Label(new Rect(10,10,100,50),"当前区域:无箭靶",labelStyle);
  56. else if(x>=-9.96&&x<10.05) GUI.Label(new Rect(10,10,100,50),"当前区域:静止靶单倍区",labelStyle);
  57. else GUI.Label(new Rect(10,10,100,50),"当前区域:移动靶双倍区",labelStyle);
  58. }
  59. public void AddRestartlabel(){//GameState=2时,展示结束界面
  60. GUI.skin = gameSkin;
  61. GUIStyle labelStyle = new GUIStyle();
  62. labelStyle.normal.textColor = Color.red;
  63. labelStyle.fontSize = 30;
  64. GUI.Label(new Rect(340,100,100,50),"最终得分:"+score,labelStyle);//展示最终得分
  65. if(GUI.Button(new Rect(340,200,160,80),"再来一次")){
  66. mainController.Restart();
  67. mainController.SetGameState((int)GameState.Playing);//按下再来一次按钮则再次进入游戏状态
  68. }
  69. }
  70. void OnGUI() {
  71. mainController.ShowPage();
  72. }
  73. }

十字弩:CrossC

挂载在十字弩物体上,控制十字弩的射击、蓄力等动画呈现,以及控制十字弩可以上下左右移动。

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. public class CrossC : MonoBehaviour
  6. {
  7. private Animator ani;
  8. public float horizontalinput;//水平参数
  9. public float Verticalinput;//垂直参数
  10. float speed=1.6f;//速度参数
  11. public float power;//蓄力系数
  12. public Arrow _arrow;//箭矢对象
  13. private Arrow arrow;
  14. private bool flag=false;
  15. private int num;
  16. private bool hold;//记录按下空格应该是拉弓蓄力还是发射
  17. private bool wait;
  18. public Text hintText;
  19. public Text powerText;
  20. void Start()//初始化
  21. { ani=GetComponent<Animator>();
  22. hintText.gameObject.SetActive(false);
  23. powerText.gameObject.SetActive(false);
  24. num=10;
  25. hold=true; wait=true;
  26. }
  27. public void Init(){
  28. num=10;
  29. }
  30. // Update is called once per frame
  31. void Update()
  32. { horizontalinput = Input.GetAxis("Horizontal");
  33. //AD方向控制
  34. Verticalinput = Input.GetAxis("Vertical");
  35. if (horizontalinput!=0&&Verticalinput!=0)
  36. {
  37. horizontalinput = horizontalinput * 1.4f;
  38. Verticalinput = Verticalinput * 1.4f;
  39. }
  40. //WS方向控制
  41. this.transform.Translate(Vector3.right * horizontalinput * Time.deltaTime * speed);
  42. //控制该物体向侧方移动
  43. this.transform.Translate(Vector3.up* Verticalinput * Time.deltaTime * speed);
  44. if(iflegalarea()) {ShowHint("您已超出射击区域,该区域无射击靶"); flag=true;}
  45. else if(flag&&!iflegalarea()) {HideHint(); flag=false;}
  46. if(power!=0f) {
  47. int ppower=(int)(power*100);
  48. powerText.text="蓄力"+ppower+"%";
  49. powerText.gameObject.SetActive(true);
  50. }
  51. else{
  52. powerText.gameObject.SetActive(false);
  53. }
  54. //控制该物体向上下移动
  55. if(Input.GetKey(KeyCode.Space))//按下空格
  56. { if(hold){//蓄力
  57. Transform childTransform = transform.Find("箭");
  58. childTransform.gameObject.SetActive(true);
  59. ani.SetBool("pull",true);
  60. ani.SetBool("shoot",false);
  61. if (power < 1)
  62. {
  63. power += Time.deltaTime * 0.5f;
  64. }
  65. else
  66. {
  67. power = 1;
  68. }
  69. ani.SetFloat("pullpower",power);
  70. wait=true;
  71. }
  72. else{//发射
  73. if(wait){ani.SetBool("shoot",true);
  74. ani.SetBool("pull",false);
  75. ani.SetBool("hold",false);
  76. initArrow();
  77. RunArrow();
  78. wait=false;
  79. Transform childTransform = transform.Find("箭");
  80. childTransform.gameObject.SetActive(false);
  81. power=0f;
  82. ani.SetFloat("pullpower",power);
  83. Invoke("SetHoldTrue", 1f);}
  84. }
  85. }
  86. if (Input.GetKeyUp(KeyCode.Space)){//抬起空格,保持
  87. ani.SetBool("pull",false);
  88. hold=false;//预备射击}
  89. }
  90. public void initArrow()//创建箭矢对象
  91. {
  92. Vector3 np=this.transform.position;
  93. arrow = Instantiate(_arrow,np,Quaternion.identity);
  94. arrow.gameObject.SetActive(true);
  95. }
  96. public void RunArrow()//发射箭矢对象
  97. {
  98. if(arrow != null)
  99. {
  100. Rigidbody rb = arrow.GetComponent<Rigidbody>();
  101. if (rb)
  102. {
  103. rb.drag = 0.5f;
  104. Vector3 shootingDirection = this.transform.forward;
  105. rb.AddForce(shootingDirection * (power*20f+5), ForceMode.Impulse);
  106. num--;
  107. }
  108. }
  109. }
  110. //向其他脚本传递数据
  111. public int getScore()//若箭矢命中,传回分数并提示
  112. {
  113. if(arrow != null && arrow.attacked&&!arrow.isRecord){
  114. arrow.isRecord=true;
  115. if(arrow.score==5) ShowHint("正中红心!",0);
  116. else ShowHint("命中!",0);
  117. float x=this.transform.position.x;
  118. if(x>=19.00&&x<=40.00) return arrow.score*2;
  119. else return arrow.score;
  120. }
  121. return 0;
  122. }
  123. public int getNum(){
  124. return num;
  125. }
  126. public float getX(){
  127. return this.transform.position.x;
  128. }
  129. public void ShowHint(string message,int state=1)//提示标签控制
  130. {
  131. // 显示提示文字
  132. hintText.text = message;
  133. hintText.gameObject.SetActive(true);
  134. // 延迟几秒后隐藏提示文字
  135. if(state==0) Invoke("HideHint", 1f);
  136. }
  137. void HideHint()
  138. {
  139. hintText.gameObject.SetActive(false);
  140. }
  141. void SetHoldTrue(){
  142. hold=true;
  143. }
  144. private bool iflegalarea(){//判断是否在射击区域,不在则提示
  145. float x=this.transform.position.x;
  146. return (x<-9.96||(x>10.05&&x<19.00)||x>40.00);
  147. }
  148. }

箭矢:Arrow

挂载在箭矢物体上,主要负责响应碰撞事件并传回击中的分数:

  1. using System.Collections;
  2. using System.Collections.Generic;
  3. using UnityEngine;
  4. using UnityEngine.UI;
  5. public class CrossC : MonoBehaviour
  6. {
  7. private Animator ani;
  8. public float horizontalinput;//水平参数
  9. public float Verticalinput;//垂直参数
  10. float speed=1.6f;//速度参数
  11. public float power;//蓄力系数
  12. public Arrow _arrow;//箭矢对象
  13. private Arrow arrow;
  14. private bool flag=false;
  15. private int num;
  16. private bool hold;//记录按下空格应该是拉弓蓄力还是发射
  17. private bool wait;
  18. public Text hintText;
  19. public Text powerText;
  20. void Start()//初始化
  21. { ani=GetComponent<Animator>();
  22. hintText.gameObject.SetActive(false);
  23. powerText.gameObject.SetActive(false);
  24. num=10;
  25. hold=true; wait=true;
  26. }
  27. public void Init(){
  28. num=10;
  29. }
  30. // Update is called once per frame
  31. void Update()
  32. { horizontalinput = Input.GetAxis("Horizontal");
  33. //AD方向控制
  34. Verticalinput = Input.GetAxis("Vertical");
  35. if (horizontalinput!=0&&Verticalinput!=0)
  36. {
  37. horizontalinput = horizontalinput * 1.4f;
  38. Verticalinput = Verticalinput * 1.4f;
  39. }
  40. //WS方向控制
  41. this.transform.Translate(Vector3.right * horizontalinput * Time.deltaTime * speed);
  42. //控制该物体向侧方移动
  43. this.transform.Translate(Vector3.up* Verticalinput * Time.deltaTime * speed);
  44. if(iflegalarea()) {ShowHint("您已超出射击区域,该区域无射击靶"); flag=true;}
  45. else if(flag&&!iflegalarea()) {HideHint(); flag=false;}
  46. if(power!=0f) {
  47. int ppower=(int)(power*100);
  48. powerText.text="蓄力"+ppower+"%";
  49. powerText.gameObject.SetActive(true);
  50. }
  51. else{
  52. powerText.gameObject.SetActive(false);
  53. }
  54. //控制该物体向上下移动
  55. if(Input.GetKey(KeyCode.Space))//按下空格
  56. { if(hold){//蓄力
  57. Transform childTransform = transform.Find("箭");
  58. childTransform.gameObject.SetActive(true);
  59. ani.SetBool("pull",true);
  60. ani.SetBool("shoot",false);
  61. if (power < 1)
  62. {
  63. power += Time.deltaTime * 0.5f;
  64. }
  65. else
  66. {
  67. power = 1;
  68. }
  69. ani.SetFloat("pullpower",power);
  70. wait=true;
  71. }
  72. else{//发射
  73. if(wait){ani.SetBool("shoot",true);
  74. ani.SetBool("pull",false);
  75. ani.SetBool("hold",false);
  76. initArrow();
  77. RunArrow();
  78. wait=false;
  79. Transform childTransform = transform.Find("箭");
  80. childTransform.gameObject.SetActive(false);
  81. power=0f;
  82. ani.SetFloat("pullpower",power);
  83. Invoke("SetHoldTrue", 1f);}
  84. }
  85. }
  86. if (Input.GetKeyUp(KeyCode.Space)){//抬起空格,保持
  87. ani.SetBool("pull",false);
  88. hold=false;//预备射击}
  89. }
  90. public void initArrow()//创建箭矢对象
  91. {
  92. Vector3 np=this.transform.position;
  93. arrow = Instantiate(_arrow,np,Quaternion.identity);
  94. arrow.gameObject.SetActive(true);
  95. }
  96. public void RunArrow()//发射箭矢对象
  97. {
  98. if(arrow != null)
  99. {
  100. Rigidbody rb = arrow.GetComponent<Rigidbody>();
  101. if (rb)
  102. {
  103. rb.drag = 0.5f;
  104. Vector3 shootingDirection = this.transform.forward;
  105. rb.AddForce(shootingDirection * (power*20f+5), ForceMode.Impulse);
  106. num--;
  107. }
  108. }
  109. }
  110. //向其他脚本传递数据
  111. public int getScore()//若箭矢命中,传回分数并提示
  112. {
  113. if(arrow != null && arrow.attacked&&!arrow.isRecord){
  114. arrow.isRecord=true;
  115. if(arrow.score==5) ShowHint("正中红心!",0);
  116. else ShowHint("命中!",0);
  117. float x=this.transform.position.x;
  118. if(x>=19.00&&x<=40.00) return arrow.score*2;
  119. else return arrow.score;
  120. }
  121. return 0;
  122. }
  123. public int getNum(){
  124. return num;
  125. }
  126. public float getX(){
  127. return this.transform.position.x;
  128. }
  129. public void ShowHint(string message,int state=1)//提示标签控制
  130. {
  131. // 显示提示文字
  132. hintText.text = message;
  133. hintText.gameObject.SetActive(true);
  134. // 延迟几秒后隐藏提示文字
  135. if(state==0) Invoke("HideHint", 1f);
  136. }
  137. void HideHint()
  138. {
  139. hintText.gameObject.SetActive(false);
  140. }
  141. void SetHoldTrue(){
  142. hold=true;
  143. }
  144. private bool iflegalarea(){//判断是否在射击区域,不在则提示
  145. float x=this.transform.position.x;
  146. return (x<-9.96||(x>10.05&&x<19.00)||x>40.00);
  147. }
  148. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/104203?site
推荐阅读
相关标签
  

闽ICP备14008679号