文章目录

  • 射箭游戏设计与实现
    • 游戏要求:
    • 具体实现代码
      • 动作部分
      • 碰撞检测
      • 工厂类生产箭
      • Controller类
      • UI类
    • 游戏效果

射箭游戏设计与实现

游戏要求:

游戏内容要求:

  1. 靶对象为 5 环,按环计分;

  2. 箭对象,射中后要插在靶上

    增强要求:射中后,箭对象产生颤抖效果,到下一次射击 或 1秒以后

  3. 游戏仅一轮,无限 trials;
    增强要求:添加一个风向和强度标志,提高难度

具体实现思路:

  1. 设计扁平圆柱体作为靶标,多个不同大小的圆柱体叠加形成不同的环,并利用颜色区分,由于叠加会影响正常显示,所以每个圆柱体的宽度(高)需要不一致,或者利用位置不同实现一个层级的效果,小的在前,大的在后就能显示出一个个环的效果。
  2. 由于对游戏轮次没有太大的要求,所以这里设置为游戏开始时拥有一定数量的箭,箭用完就算结束,显示得分,由用户决定是否再来一次(对于无限trails的规则不太清楚)。
  3. 风向则可是使用一个持续添加在箭上的力来实现。

具体实现代码

首先用到之前几个游戏的一些基类:Director、SSAction、SSActionManager、Singleton等,这几个类由于只是基类,真正的逻辑实现都在子类中,所以直接重用。

动作部分

先来说说游戏的动作部分,首先分析主要的运动对象:箭。。。没了。所以与之前打飞碟的游戏类似,只需要实现将箭飞出去的动作,通过物理引擎的实现之前也提到过,只需要在出去的瞬间给它添加一个力就可以了。
其次是风力的作用,与飞出去的瞬间力不同,风力是需要持续作用的力,所以需要在FixedUpdate里持续添加。
从以上可以看出,此动作类主要的关键属性有:运动方向、风力作用方向。
ArrowAction 代码如下:

public class ArrowAction : SSAction{public Vector3 force;public Vector3 affect;public static ArrowAction GetSSAction(Vector3 f, Vector3 wind) {ArrowAction action = ScriptableObject.CreateInstance<ArrowAction>();action.force = f;action.affect = wind;return action;}public override void FixedUpdate() {this.gameObject.GetComponent<Rigidbody>().AddForce(affect, ForceMode.Acceleration);if (this.transform.position.z > 3 ||  Mathf.Abs(this.transform.position.y) > 7 || Mathf.Abs(this.transform.position.x) > 10 || this.gameObject.tag == "ontarget") {this.destroy = true;if (this.gameObject.tag != "ontarget")this.callBack.SSActionEvent(this);}}public override void Update(){}public override void Start() {this.gameObject.GetComponent<Rigidbody>().velocity = Vector3.zero;this.gameObject.GetComponent<Rigidbody>().AddForce(force, ForceMode.Impulse);}
}

所以构造此类的时候需要的参数也就是两个,分别对应两个关键属性。
此外,当箭超出一定范围时,我们就可以认为它无法到达靶标,直接中止动作执行。或者当其到达目标时,(名字会设置为ontarget)也可以中止动作。

动作管理器类也就是简单的包装一下动作类,并且使其执行,并且实现回调函数,这里的回调就是用弓箭工厂的方法将箭的对象释放掉(加入到free的队列去,以便重新使用):
ArrowActionManager

public class ArrowActionManager : SSActionManager, ISSActionCallback {ArrowAction arrowAction;Controller controller;private void Start(){controller = Director.getInstance().currentSceneController as Controller;controller.actionManager = this;}public void arrowFly(GameObject arrow, Vector3 target, Vector3 wind) {arrowAction = ArrowAction.GetSSAction(target, wind);if (arrow.GetComponent<Rigidbody>() == null)arrow.AddComponent<Rigidbody>();else arrow.GetComponent<Rigidbody>().isKinematic = false;this.RunAction(arrow, arrowAction, this);}public void SSActionEvent(SSAction action){Singleton<ArrowFactory>.Instance.freeArrow(action.gameObject);if (controller.arrow.name == "arrow")controller.getArrow();}
}

值得注意的一点是,由于箭未发出的时候,是需要在弓上停留的,也即是说不能有刚体的重力作用,否则就会掉下去。所以在运动管理器中,需要在执行射箭动作之前,恢复刚体的作用,这里采用的是运动学模式和物理模式切换的方式来实现。如果是新创建的实例,还没有添加刚体部件则需要添加。
回调函数里主要是运动结束后执行的行为,运动结束主要有两个状态,上靶和未中靶,上靶的箭不能free掉,因为要保留来积分(模拟真实场景),所以还需要额外判断当前的箭是脱靶了还是中靶的。

碰撞检测

主要是利用碰撞体的Trigger,来检测是否碰撞到了,如果碰到了就积分,不同的碰撞体不同分,然后将箭的名字(用来代表状态)改成ontarget,这样就防止别的碰撞器也重复积分。因为会实际上两个碰撞器的距离很微小,所以能够同时碰到多个碰撞器。

public class CollisionRev : MonoBehaviour {private void OnTriggerEnter(Collider other){   GameObject arrow = other.gameObject;if (arrow.name == "arrow") {string str = this.name;arrow.GetComponent<Rigidbody>().velocity = Vector3.zero;arrow.GetComponent<Rigidbody>().isKinematic = true;Singleton<Judger>.Instance.addScore(str);arrow.transform.position += Vector3.forward * 0.001f;arrow.name = "ontarget";Controller controller = Director.getInstance().currentSceneController as Controller;controller.hit(arrow);// if (controller.arrow == null)controller.getArrow();}}
}

工厂类生产箭

与前一个实验的飞碟工厂很像,而且不需要添加属性什么的,更加简单。
只需要将空闲的或者刚创建的箭,初始化位置等就可以了。还有一个Free的方法,也是跟之前类似。

public class ArrowFactory : MonoBehaviour {public GameObject arrow = null;private List<GameObject> activeList = new List<GameObject>();private List<GameObject> freeList = new List<GameObject>();public GameObject getArrow() {if (freeList.Count > 0) {arrow = freeList[0].gameObject;freeList.Remove(freeList[0]);arrow.GetComponent<Rigidbody>().isKinematic = true;}else {arrow = Instantiate(Resources.Load("Prefabs/arrow", typeof(GameObject))) as GameObject;}arrow.transform.rotation = Quaternion.Euler(0,0,0);arrow.transform.position = new Vector3(-0.1f, 0.85f, -9.7f);arrow.SetActive(true);arrow.name = "ready";activeList.Add(arrow);return arrow;}public void freeArrow(GameObject a) {for (int i  = 0; i < activeList.Count; i ++) {if (a.GetInstanceID() == activeList[i].gameObject.GetInstanceID()) {activeList[i].gameObject.SetActive(false);freeList.Add(activeList[i]);activeList.Remove(activeList[i]);break;}}}
}

Controller类

public class Controller : MonoBehaviour, SceneController, Interaction
{public ArrowActionManager actionManager;public ArrowFactory factory;public GameObject bow;public GameObject target;public GameObject arrow;public Judger judger;public Vector3 direction;public UI ui;public int state = 0;private int arrowNumber = 0;private Queue<GameObject> hit_arrow = new Queue<GameObject>();public Vector3 wind = Vector3.zero; private int[] direc = {1,-1,0};private void Start() {Director director = Director.getInstance();director.currentSceneController = this;factory = this.gameObject.AddComponent<ArrowFactory>();actionManager = this.gameObject.AddComponent<ArrowActionManager>() as ArrowActionManager;ui = this.gameObject.AddComponent<UI>();judger = this.gameObject.AddComponent<Judger>();// factory = Singleton<ArrowFactory>.Instance;loadResources();int x = Random.Range(0,3);int y = Random.Range(0,3);x = direc[x];y = direc[y];int level = Random.Range(1,5);wind = new Vector3(x, y, 0) * level;}public void loadResources() {bow = Instantiate(Resources.Load("Prefabs/bow", typeof(GameObject))) as GameObject;target = Instantiate(Resources.Load("Prefabs/target", typeof(GameObject))) as GameObject;arrow = factory.getArrow();}private void Update(){}public void moveArrowDirection(Vector3 to) {if (state <= 0) {return;}arrow.transform.rotation = Quaternion.LookRotation(to);bow.transform.rotation = Quaternion.LookRotation(to);direction = to;}public void reuse() {int tmp = hit_arrow.Count;for (int i = 0; i < tmp; i ++) {factory.freeArrow(hit_arrow.Dequeue());}arrowNumber = 0;}public void shoot(Vector3 force) {if (state > 0 && arrow != null) {// arrow = factory.getArrow();arrow.name = "arrow";actionManager.arrowFly(arrow, direction * 15, wind);arrowNumber ++;}}public void hit(GameObject arrow) {hit_arrow.Enqueue(arrow);}public void getArrow() {int x = Random.Range(0,3);int y = Random.Range(0,3);x = direc[x];y = direc[y];int level = Random.Range(1,5);wind = new Vector3(x, y, 0) * level;if (state == 1) {if (arrowNumber > 7) {setState(-1);}}arrow = factory.getArrow();}public void setState(int s) {state = s;}public void restart() {state = 0;arrowNumber = 0;}public int getState() {return state;}public string arrowState() {return arrow != null ? arrow.name : null;}public Vector3 getWind() {return wind;}
}

这里面主要是实现了射箭、获取箭的函数,还有一部分与用户交互的函数。
loadResources就是加载弓箭和靶子的资源。
射箭shoot主要是状态的改变,之前已经说过,用名字来代表状态,在射出的时候更改状态为arrow表示在飞行中。
moveDirection函数主要是更改弓箭的朝向,这里是与鼠标的位置相关,也就是利用鼠标更改朝向,使得其能够瞄准。
getArrow则是获取弓箭,也就是下一次射箭的准备工作,需要把风向提前设置好,这里使用随机的方式生成8个方向的风,风力等级也是随机,有4个等级。
hit则是类似回调,将中箭的加入使用中的队列,因为这部分箭不会自动收回(只收回了脱靶的)
还有一些获取状态的函数,游戏状态、风力信息、弓箭状态等。

UI类

UI主要是负责用户的交互,所以需要获取Controller的一些状态来判断用户操作是否合法或者限制用户的操作。

public class UI : MonoBehaviour {Interaction interaction;bool flag = true;GUIStyle style1;GUIStyle style2;GUIStyle style3;float time = 0;private void Start() {interaction = Director.getInstance().currentSceneController as Interaction;style1 = new GUIStyle("button");style1.fontSize = 25;style2 = new GUIStyle();style2.fontSize = 35;style2.alignment = TextAnchor.MiddleLeft;style3 = new GUIStyle();style3.fontSize = 15;style3.alignment = TextAnchor.MiddleLeft;}private void OnGUI(){   if (interaction.getState() == -1) {if (time < 2) {time += Time.deltaTime;GUI.Label(new Rect(Screen.width/2-70, Screen.height/2-135, 200, 30), "Preparing Arrow...", style2);} else {GUI.Label(new Rect(Screen.width/2-70, Screen.height/2-135, 200, 30), "Your Score:" + Singleton<Judger>.Instance.getScore().ToString(), style2);if (GUI.Button(new Rect(Screen.width/2-70, Screen.height/2 - 20, 180, 70), "Play again", style1)) {interaction.reuse();interaction.setState(1);Singleton<Judger>.Instance.restart();time = 0;}}}GUI.Label(new Rect(5, 5, 100, 30), "Score: " + Singleton<Judger>.Instance.getScore().ToString(), style3);Vector3 wind = interaction.getWind();int x = (int)wind.x;int y = (int)wind.y;string str1, str2, level;if (x < 0) str1 = "West";else if (x > 0)str1 = "East";else str1 = "";if (y < 0) str2 = "South";else if (y > 0)str2 = "North";else str2 = "";if (x == 0 && y == 0) {str1 = "No wind";}if (x != 0) {int tmp = x > 0 ? x : -x;level = tmp.ToString();} else if (y != 0) {int tmp = y > 0 ? y : -y;level = tmp.ToString();} else {level = "0";}GUI.Label(new Rect(5, 35, 200, 30), "Wind Direction: " + str1 + str2, style3);GUI.Label(new Rect(5, 65, 100, 30), "Wind Level: " + level , style3);if (flag) {GUI.Label(new Rect(Screen.width/2-60, Screen.height/2-135, 100, 50), "ShootArrow!", style2);if(GUI.Button(new Rect(Screen.width/2-70, Screen.height/2 - 20, 150, 70), "Play", style1)) {flag = false;interaction.setState(1);}}}private void Update(){if (interaction.getState() > 0) {Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);if (interaction.arrowState() == "ready") {interaction.moveArrowDirection(ray.direction);if (Input.GetButtonDown("Fire1")) {interaction.shoot(ray.direction);}}}}
}

这里主要是根据Controller的状态(游戏未开始、进行中、结束)来显示不同的界面,如果是进行中,还需要显示分数、风力信息等,这里是通过Controller的风力向量,临时计算风力信息,有点不太好。
还有最重要的一点是,获取鼠标位置,并且设置相应的弓箭朝向。响应点击事件来射箭,具体也就是调用Controller的接口来执行动作。

游戏效果

本次实验到此结束!

交互入门2——射击打靶游戏相关推荐

  1. unity 开发射击打靶vr_【好玩微信小游戏大全】香肠派对:点开既玩的射击小游戏!正版授权...

    沐沐带你发现好游戏! 沐沐今天推荐这款游戏叫<香肠派对>, 你以为是这款吃鸡的APP游戏吗? 大错特错! 沐沐我怎么会这么俗呢? 要推荐就推荐你没玩过的! 今天推荐这款<香肠派对&g ...

  2. python3小游戏源代码_Python入门塔防小游戏,开发步骤和源码,带你轻松学python...

    在上一篇文章中,我介绍了有关python入门塔防小游戏的一些功能,下面我继续讲解有关这个小游戏的大致开发步骤. 开发工具还是: Python版本:3.6.4 相关的模块有:pygame模块,以及一些p ...

  3. 游戏开发入门(五)游戏动画系统

    视频链接:游戏开发入门(五)游戏动画系统(8节课 时常:约2小时30分钟) PPT比视频要块一些 第二节与PPT对不上 视频中杂音太大,如果带耳机还听不清的话,建议直接看内容即可 笔记与总结(请先学习 ...

  4. Java面向对象(6) —— 射击小游戏

    这里做一个射击小游戏,作为对前面Java学习的一个小结. 1.设计 2.知识点总结 主要是在主程序ShootGame.java中引入了一些新知识,swing画图.定时器.鼠标事件处理.其他程序则主要体 ...

  5. 【青少年编程】陈晓光:打靶游戏

    「青少年编程竞赛交流群」已成立(适合6至18周岁的青少年),公众号后台回复[Scratch]或[Python],即可进入.如果加入了之前的社群不需要重复加入. 微信后台回复"资料下载&quo ...

  6. C#游戏开发快速入门2.2改变游戏对象的状态

    C#游戏开发快速入门2.2改变游戏对象的状态 改变游戏对象的状态,就是要改变游戏对象的位置.朝向和大小.那么,为什么要改变游戏对象的状态呢?当然是因为游戏对象的状态不合适了.在具体说明之前,读者应该先 ...

  7. C#游戏开发快速入门 2.1 构建游戏场景

    C#游戏开发快速入门 2.1  构建游戏场景 如果已经计划好了要编写什么样的游戏,在打开Unity以后,要做的第一件事情就是构建游戏场景(Scene).游戏场景就是玩家游戏时,在游戏视图中看到的一切, ...

  8. 基于pygame的射击小游戏制作(一)让飞船动起来

    基于pygame的射击小游戏制作(一)让飞船动起来 一.文件结构 alien_invasion.py 是整个系统的主文件,用来创建游戏中的一系列对象,ai_settings存储设置.screen存储显 ...

  9. 非常强大的射击动作游戏源码完整版

    一款横版射击动作游戏.左下方有一个摇杆,可以控制物体作前进和跳跃,点击屏幕的左边,可以进行射击.游戏采用的是2D动画,可玩性还是不错的.使用ios4,ios5,ios6模拟器运行都没问题,但是在本人的 ...

最新文章

  1. 2021 年 NLP 重要国际会议时间
  2. css中font-family的中文字体
  3. c#链接操作mysql数据库代码_c#连接mysql数据库 代码
  4. 「LOJ 2289」「THUWC 2017」在美妙的数学王国中畅游——LCT泰勒展开
  5. Python二级笔记(9)
  6. java nextlong_Java Random nextLong()方法与示例
  7. 【转】const int *p和int * const p的区别(常量指针与指向常量的指针)
  8. Linux——查看系统的日志的一些实用操作
  9. solaris中如何新建一个用户登录
  10. MySQL自动生成Oracle建表语句脚本
  11. sg90舵机c语言编程,浅谈用单片机控制SG90舵机(原理+编程)
  12. REST Assured 系列汇总
  13. wei.getA()
  14. python3.7程序实例_生信编程实战第7题(python)
  15. Excel操作:制作to do list
  16. 抖音爱心c语言程序,抖音上用记事本编写爱心小程序教程
  17. SAP中物料历史标准成本清单
  18. Ubuntu20.04更换软件源之后apt-get update报错
  19. Maven发布轻量二方包
  20. cmnet 和 cmwap 收费等区别

热门文章

  1. 《白鹿原》电视剧观后感
  2. 织梦教程新建ID丨重置栏目ID丨文章ID不从1开始的解决方法
  3. innodb buffer pool管理--free list
  4. 传统企业如何建设B2C平台做网络营销?
  5. 【漫漫转码路】Day 38 C++ day09
  6. 有些市场上卖的卤牛肉40元一斤,是真牛肉吗?
  7. 钱宝订单查询 chaqb.com
  8. 生物计算机未来,未来计算机可能朝着量子计算机、光子计算机和生物计算机等方向发展。(  )...
  9. cap分析zk是cp还是ap
  10. laravel:artisan的基本使用