打靶游戏要求:

1.靶对象为5环,按环计分;
2.箭对象,射中后要插在靶上;
3.游戏仅一轮,无限trials;
4.增强要求:添加一个风向个强度标志,提高难度;

游戏演示:

代码结构:

设计思路与细节:

1.构建基本游戏对象:

这里的主要游戏对象有两个,分别是靶子和箭。首先说一下靶子,如何通过unity的基本游戏对象创建一个靶子使得箭可以识别靶的不同环?在这里我通过将几个半径不同的扁圆柱体共圆心,且每一个的厚度由内向外递减(以比较小的差别),再配以不同颜色,就生成了一个近似现实世界的靶子。
下面是靶的各个部分的详细属性,其中bart为空对象,用来裝靶的各个部分,每一个部分有独立的碰撞器,这里我用Mesh Collider,这样方便我们后面识别箭打在靶上时打到了第几环。
然后是箭的设计,在这里我用几个基本物体组合起来,再调节一下大小和位置属性,其中脚本ArrowScript挂在箭上,用于识别触发并将信息传入ScoreRecorder进行计分。

2.射出箭矢:

在这里,我的思路是点击屏幕,箭矢能够向我所点击的方向射出。在这里,我用课上老师所讲的射线Ray来实现。在屏幕中(相机为透视模式),屏幕每一个点p对应标准面上的一个点p',射线就利用这个原理。我通过摄像机调用ScreenPointToRay函数,能得到从相机通过鼠标所点的屏幕点的光线,这样我们就有了一个理想的射击方向。代码实现如下(在UserInterface中):
        if (Input.GetMouseButtonDown(0)) {Ray mouseRay = camera.ScreenPointToRay (Input.mousePosition);Debug.Log("RayDir = " + mouseRay.direction);Controller.shootArrow (mouseRay.direction);}

顺便,我还实现了箭头也能面向点击方向,只需设置箭的up或forward等为射线方向即可(具体根据箭对象的头是up还是forward设置)。

3.触发识别(ArrowScript与ScoreRecorder):

在这里我用OnTriggerEnter来实现箭与靶碰撞的识别。根据上面介绍,我的靶是不同半径的圆柱体重叠而成,因为帧数与运动的原因,这样就有可能触发多个不同的环。那我就储存第一个碰到的环即可,只要曾经存过,就不再存储(我存环的名字来区别不同的环,为bartNum)。ArrowScript中代码如下:
    void OnTriggerEnter(Collider other) {;
        if (bartNum == "") {
            bartNum = other.gameObject.name;
            Debug.Log ("arrowTrigger = " + bartNum);            Destroy (GetComponent<Rigidbody>()); //使得箭停在靶子上
            Component []comp = GetComponentsInChildren<CapsuleCollider>();
            foreach (CapsuleCollider i in comp) {
                i.enabled = false;
            }
            //使得箭不会触发靶子上的箭
            GetComponent<MeshCollider>().isTrigger = false;
            recorder.countScore (bartNum);
            //记录分数
        }
    }

ScoreRecorder中相关代码如下:

    public void countScore(string type) {if (type == "circle01")Score += 10;else if (type == "circle02")Score += 8;else if (type == "circle03")Score += 6;else if (type == "circle04")Score += 4;else if (type == "circle05")Score += 2;Debug.Log ("Score = " + Score);}

4.计时机制回收箭矢(ArrowScript和ArrowFactory):

因为要求箭矢要留在靶上,所以决不能在一碰撞就回收箭矢。为了实现工厂回收功能,我通过设置一个计时器来满足两个需求。ArrowScript中的代码如下:
        private float time; //计时器void Update () {time += Time.deltaTime;if (time >= LIMITTIME) {this.gameObject.SetActive (false);initial ();//重置变量值}}

ArrowFactory中相关代码如下:

    private void freeArrow() {for (int i = 0; i < UsedArrow.Count; i++) {GameObject temp = UsedArrow [i];if (!temp.activeInHierarchy) {UsedArrow.RemoveAt (i);FreeArrow.Add (temp);}}}void Update () {Debug.Log ("Used :" + UsedArrow.Count);Debug.Log ("Free :" + FreeArrow.Count);freeArrow ();}

完整代码:

1.UserInterface:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;public class UserInterface : MonoBehaviour {private SceneController Controller;public Camera camera;public Text Score;public Text WindForce;public Text WindDirection;// Use this for initializationvoid Start () {Controller = (SceneController)FindObjectOfType(typeof(SceneController));Debug.Log ("Controller = "+ Controller);}// Update is called once per framevoid Update () {Score.text = "Score : " + Controller.getRecorder ().getScore (); //显示分数float force = Controller.getActionManager ().getWindForce ();if (force < 0) {WindDirection.text = "Wind Direction : Left";} else if (force > 0) {WindDirection.text = "Wind Direction : Right";} else {WindDirection.text = "Wind Direction : No Wind";}
     //显示风向WindForce.text = "Wind Force : " + Controller.getActionManager ().getWindForce (); //显示风力if (Input.GetMouseButtonDown(0)) {Ray mouseRay = camera.ScreenPointToRay (Input.mousePosition);Debug.Log("RayDir = " + mouseRay.direction);Controller.shootArrow (mouseRay.direction);//以点击的方向发射箭矢}}
}
2.Director:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Director : System.Object {private static Director _instance;public SceneController Controller{ get; set;}public static Director getinstance() {if (_instance == null) {_instance = new Director ();}return _instance;}public int getFPS() {return Application.targetFrameRate;}public void setFPS(int fps) {Application.targetFrameRate = fps;}
}

3.SceneController:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SceneController : MonoBehaviour {private ActionManager actionManager;private ScoreRecorder scoreRecorder;private ArrowFactory arrowFactory;void Awake() {Director director = Director.getinstance ();director.setFPS (60);director.Controller = this;actionManager = (ActionManager)FindObjectOfType (typeof(ActionManager));scoreRecorder = (ScoreRecorder)FindObjectOfType (typeof(ScoreRecorder));arrowFactory = (ArrowFactory)FindObjectOfType (typeof(ArrowFactory));Debug.Log("Controller");}public ArrowFactory getFactory() {return arrowFactory;}public ScoreRecorder getRecorder() {return scoreRecorder;}public ActionManager getActionManager() {return actionManager;}public void shootArrow(Vector3 dir) {GameObject arrow = arrowFactory.getArrow (); //获得箭矢arrow.transform.position = new Vector3(0, 2, 0); //设置箭矢初始位置actionManager.shoot (arrow, dir);//调用ActionManager实现动作细节}
}

4.ArrowFactory:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ArrowFactory : MonoBehaviour {private List<GameObject> UsedArrow; //储存使用中的箭矢private List<GameObject> FreeArrow; //储存空闲箭矢private GameObject arrowPrefab;void Awake() {arrowPrefab = Instantiate(Resources.Load ("Prefabs/arrow")) as GameObject;arrowPrefab.SetActive (false);FreeArrow = new List<GameObject> ();UsedArrow = new List<GameObject> ();Debug.Log ("ArrowFactory");}// Update is called once per framepublic GameObject getArrow() {GameObject temp;if (FreeArrow.Count == 0) {temp = GameObject.Instantiate (arrowPrefab) as GameObject;temp.SetActive (true);
           //如果空闲箭矢中没有箭矢,就生成新的箭矢} else {temp = FreeArrow [0];temp.SetActive (true);if (temp.GetComponent<Rigidbody>() == null)temp.AddComponent<Rigidbody> ();Component []comp = temp.GetComponentsInChildren<CapsuleCollider>();foreach (CapsuleCollider i in comp) {i.enabled = true;}temp.GetComponent<MeshCollider>().isTrigger = true;Debug.Log ("temp = " + temp);FreeArrow.RemoveAt (0);
           //空闲箭矢中有箭矢,初始化相关属性,加入使用}UsedArrow.Add (temp);return temp;}private void freeArrow() {for (int i = 0; i < UsedArrow.Count; i++) {GameObject temp = UsedArrow [i];if (!temp.activeInHierarchy) {UsedArrow.RemoveAt (i);FreeArrow.Add (temp);}}}void Update () {Debug.Log ("Used :" + UsedArrow.Count);Debug.Log ("Free :" + FreeArrow.Count);freeArrow ();}
}

5.ActionManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ActionManager : MonoBehaviour {private float speed = 40f; //箭矢初速private float Force = 0f; //第一箭风力默认为0void Awake() {Debug.Log ("ActionManager");}public void shoot(GameObject arrow, Vector3 dir) {arrow.transform.up = dir;//设置一下箭的朝向arrow.GetComponent<Rigidbody> ().velocity = dir * speed; //设置箭矢的速度。wind (arrow);Force = Random.Range(-100,100); //获取随机的风力}private void wind(GameObject arrow) {Debug.Log ("AddForce");arrow.GetComponent<Rigidbody> ().AddForce (new Vector3 (Force, 0, 0), ForceMode.Force); //对箭矢施加恒定的风力}public float getWindForce() {return Force;}
}

6.ScoreRecorder:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ScoreRecorder : MonoBehaviour {private int Score = 0;//初始分数为0public void countScore(string type) {if (type == "circle01")Score += 10;else if (type == "circle02")Score += 8;else if (type == "circle03")Score += 6;else if (type == "circle04")Score += 4;else if (type == "circle05")Score += 2;Debug.Log ("Score = " + Score);
      //不同环不同分}public int getScore() {return Score;}
}

7.ArrowScript:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ArrowScript : MonoBehaviour {private string bartNum;private ScoreRecorder recorder;private float time; //计时器private static float LIMITTIME = 4;void initial() {time = 0;bartNum = "";}void OnTriggerEnter(Collider other) {;if (bartNum == "") {bartNum = other.gameObject.name;Debug.Log ("arrowTrigger = " + bartNum);Destroy (GetComponent<Rigidbody>()); //使得箭停在靶子上Component []comp = GetComponentsInChildren<CapsuleCollider>();foreach (CapsuleCollider i in comp) {i.enabled = false;}//使得箭不会触发靶子上的箭GetComponent<MeshCollider>().isTrigger = false;recorder.countScore (bartNum);//记录分数}}public string getBartNum() {return bartNum;}// Use this for initializationvoid Awake () {initial ();recorder = (ScoreRecorder)FindObjectOfType (typeof(ScoreRecorder));}// Update is called once per framevoid Update () {time += Time.deltaTime;if (time >= LIMITTIME) {this.gameObject.SetActive (false);initial ();//重置变量值}}
}

Unity3DGame学习笔记(4):射箭游戏相关推荐

  1. Unity学习笔记—二次元日系游戏制作(实践篇-游戏初始化场景制作)

    原教程:siki:二次元日系游戏制作工具 - live2dSDK入门教程 http://www.sikiedu.com/my/course/282 (上)Unity学习笔记-二次元日系游戏制作(理论篇 ...

  2. [Unity学习笔记:FPS游戏制作(3)]子弹拖尾,碰撞与枪口火焰效果

    往期博客[Unity学习笔记:FPS游戏制作(2)] 发射子弹----(2021.6.20学习笔记) 文章目录 一,实现思路 二,粒子效果的实现 (1)子弹拖尾特效的实现 (2)枪口火焰特效的实现 ( ...

  3. [Unity学习笔记:FPS游戏制作(2)] 发射子弹————(2021.6.20学习笔记)

    往期博客 [Unity学习笔记:FPS游戏制作(1)]角色的移动,旋转与推进上升----(2021.6.13学习笔记) 文章目录 一,实现思路 二,实现代码 三,脚本的使用方法 四,最终效果 一,实现 ...

  4. Pygame学习笔记 6 —— 3D游戏

        pygame是是上世纪的产品,虽然不适合最3D游戏,但我可以使用pygame来绘制简单的3D图形,就像在白纸上画立体图形一样. 主要内容: 视觉上的远近.3D空间.绘制一个空间图形 一.视觉上 ...

  5. UE5 GAS 学习笔记 1.2游戏标签

    文章目录 前言 一.概念介绍 二.响应Gameplay Tags的变化 UE5 GAS 学习笔记目录 前言 这一节对GameplayTag游戏标签进行详细的讲解. 一.概念介绍 FGameplayTa ...

  6. unity学习笔记2-卡牌游戏的制作

    unity学习笔记2 需求:制作卡牌游戏 第一步,建立card类,carddatabase类 card类删除自带的start和update方法,因为只是个对象类,负责装载对象即可 carddataba ...

  7. HTML5中canvas实现拼图游戏,HTML5 Canvas学习笔记(6)拼图游戏(数字版)

    今天网上发现了一段代码,只有界面,很不错,学习了并完成了逻辑. 效果图: 点击这里试玩 http://www.108js.com/article/canvas/6/play.html 欢迎访问博主的网 ...

  8. Unity学习笔记—二次元日系游戏制作(理论篇)

    原教程:siki:二次元日系游戏制作工具 - live2dSDK入门教程 http://www.sikiedu.com/my/course/282 一.准备工作 1.下载安装:Live2D_Cubis ...

  9. C++学习笔记——天天爱消除游戏实践

    最近了一个天天爱消除的小游戏,以此来学习C++以及"graphic"头文件的应用,此程序参考了网上大神编写的程序源码. 一.代码编写 首先我们在VS2015中新建工程,首先编写头文 ...

最新文章

  1. iOS 命令行自动打包 (archive)
  2. 3D特效师可以下班了丨Science
  3. 80%的企业社会化商务应用可能无法取得预期效果
  4. IDEA Project Structure 配置说明
  5. HPU暑期第五次积分赛 F - 辞树的质因数分解
  6. go build -X 的妙用
  7. php smarty 序号,php – Smarty缩进和代码格式
  8. 身份验证和授权作为开源解决方案服务
  9. 使用FastReport报表工具生成图片格式文档
  10. 解决QQ未启用状态,QQ留言图标未启用
  11. python通过txt文件将指定图片复制到另一个文件夹
  12. iframe加载完成后操作contentDocument
  13. 华为认证hcia含金量_华为hcna认证含金量高吗?
  14. 在页面上动态显示实时时间
  15. Arduino传感器实验清单
  16. 手机下载的Termux如何利用you-get 下载视频
  17. C#开启和关闭UAC功能
  18. Apache网页与安全优化
  19. 利用qq账号查询所绑定的手机号码_怎么不用手机号注册新微信账号 免手机号申请方法分享...
  20. BZOJ1189 [HNOI2007]紧急疏散evacuate

热门文章

  1. 分享一个计算机视觉的交流群
  2. 如何解决IP地址发生冲突故障?
  3. 0929——微信思维导图(通讯录)
  4. 读书笔记-- 京东电商推荐系统
  5. UTF-8编码规则解析
  6. UE4-游戏框架——GameMode、GameState、PlayerState、Controller、Pawn
  7. 优漫动游B端UI设计师的交互文档应该怎么写?
  8. php如何把字符串拆成一个一个的,php 将一个字符串分割为组成它的字符
  9. php以 /n 分割字符串
  10. Android图片库Fresco