[Unity3D]自己动手重制坦克舰队ArmadaTank(2)从碰撞说起

[Unity3D]自己动手重制坦克舰队ArmadaTank(2)从碰撞说起

上一篇里我给出了重制的坦克舰队效果图和试玩程序。本篇介绍一下玩家坦克和敌方坦克碰撞问题。

+BIT祝威+悄悄在此留下版了个权的信息说:

我们需要什么样的碰撞

原版里,玩家与其它坦克碰撞时,玩家与对方都不能移动;而敌方坦克之间相互碰撞时,是无视碰撞直接穿透的。这些功能的实现需要一些特殊的设计。

需要注意到,坦克舰队里的坦克移动方式是以格为单位的,每次移动都会移动完整的1个单位。就是说,坦克在下图所示的D字母里面只有上边和下边两个停留位置,而不会在中间某处停留。

据此,我们给出实现方法。

障碍物检测法

在游戏画面中,看到的坦克是这样的:

但是为了实现原版的碰撞效果,我们给每个坦克都加上前后左右4个cube作为障碍物检测器(Obstacle Detector),让他们分别检测坦克所在位置的前后左右是否存在障碍物。

为了避免与子弹碰撞,我们把这些cube压扁了,其高度值只有0.1,而长宽仍为1。

此外,这些cube最终目的是检测坦克的存在,所以要给坦克本身一个Collider。

注意,这里我们让敌方坦克自身的Collider半径稍微小于0.5,以便后续的实现。

而玩家坦克的Collider半径保持0.5不变。

你会看到,这样的微小差异,会使敌方坦克之间的碰撞检测稍微迟钝一点,所以他们就可以相互穿透;而敌方坦克与玩家坦克之间仍然不可穿透。

Cube的作用

cube的作用是这样的:每个cube内部维护一个List<GameObject> obstacles列表,这个列表记录此检测器遇到的所有障碍物。每当触发OnTriggerEnter(Collider other)时,就把other指定的物体(障碍物)加入obstacles列表;每当触发OnTriggerExit(Collider other)时,就把other指定的物体(障碍物)移出obstacles列表。这样,就可以通过分析obstacles里的情况判断出此cube代表的方向(前后左右之一)是否有障碍物了。

 1 public abstract class ObstacleDetector : MonoBehaviour {
 2
 3     public System.Collections.Generic.List<GameObject> obstacles;
 4
 5     void Awake()
 6     {
 7         if (obstacles == null)
 8         {
 9             obstacles = new System.Collections.Generic.List<GameObject>();
10         }
11     }
12
13     protected abstract void OnTriggerEnter(Collider other);
14
15     void OnTriggerExit(Collider other)
16     {
17         this.obstacles.Remove(other.gameObject);
18     }
19
20     public bool IsUnblocked()
21     {
22         for (int i = 0; i < this.obstacles.Count; i++)
23         {
24             if(this.obstacles[i] != null && this.obstacles[i].collider.enabled)
25             {
26                 return false;
27             }
28         }
29         return true;
30     }
31 } 

敌方坦克的障碍物检测器

所有类型的敌方坦克的碰撞效果都相同,所以统一用下面的脚本:

 1 public class EnemyObstacleDetector : ObstacleDetector {
 2
 3     protected override void OnTriggerEnter(Collider other)
 4     {
 5         var tag = other.tag;
 6         if (tag != null)
 7         {
 8             if (tag == Tags.EnemyObstacleDetector) { return; }
 9             if (tag == Tags.PlayerObstacleDetector) { return; }
10             // 对敌方坦克来说,另一个敌方坦克是可以穿透过去的,所以不应加入obstacles列表。
11             if (tag == Tags.EnemyObstacleDetectorCenter) { return; }
12         }
13
14         this.obstacles.Add(other.gameObject);
15     }
16 }

对敌方坦克来说,另一个敌方坦克是可以穿透过去的,所以不应加入obstacles列表。

如果碰到的是别人的cube(障碍物检测器),直接忽略即可。

玩家坦克的障碍物检测器

玩家坦克与敌方坦克的碰撞效果不同,所以需要单独处理。

 1 public class PlayerObstacleDetector : ObstacleDetector
 2 {
 3
 4     protected override void OnTriggerEnter(Collider other)
 5     {
 6         var tag = other.tag;
 7         if (tag != null)
 8         {
 9             if (tag == Tags.EnemyObstacleDetector) { return; }
10             if (tag == Tags.PlayerObstacleDetector) { return; }
11         }
12
13         this.obstacles.Add(other.gameObject);
14     }
15
16 }

对玩家坦克来说,其它坦克是不能穿透的。

同样,如果碰到的是别人的cube(障碍物检测器),直接忽略即可。

继承与GetComponent

ObstacleDetector是一个抽象基类,敌方坦克和玩家坦克分别使用的是其子类EnemyObstacleDetector和PlayerObstacleDetector。此时,GetComponent<T>()仍然可以正常使用。它返回一个基类对象的引用,此引用实际指向的则是某个子类的对象。

这是一个通用的知识。

 1 public class TankTranslate : MonoBehaviour
 2 {
 3     private System.Collections.Generic.Dictionary<TankToward, ObstacleDetector> obstacleDetectorDict;
 4
 5     // Use this for initialization
 6     void Start()
 7     {
 8         if (obstacleDetectorDict == null)
 9         { obstacleDetectorDict = new System.Collections.Generic.Dictionary<TankToward, ObstacleDetector>(); }
10         var names = new string[] { "forward", "backward", "left", "right" };
11         var direction = new TankToward[] { TankToward.Z, TankToward.NZ, TankToward.NX, TankToward.X };
12         for (int i = 0; i < names.Length; i++)
13         {
14             var child = this.transform.FindChild(names[i]);
15             var script = child.GetComponent<ObstacleDetector>();
16             obstacleDetectorDict.Add(direction[i], script);
17         }
18     }
19 } 

起火冒烟

坦克被击中会损失health值,视觉效果上我们用冒烟的浓度显示。

冒烟效果用particle System组件来做。

控制冒烟的浓度需要一个脚本。

 1 public class TankSmoke : MonoBehaviour
 2 {
 3     public TankHealth tankHealthScript;
 4     private float lastHealth;
 5
 6     // Update is called once per frame
 7     void Update()
 8     {
 9         if (lastHealth != tankHealthScript.health)
10         {
11             var lostHealth = (TankHealth.maxHealth - tankHealthScript.health);
12             if (lostHealth <= 0)
13             {
14                 this.particleSystem.enableEmission = false;
15             }
16             else
17             {
18                 this.particleSystem.Play();
19                 this.particleSystem.enableEmission = true;
20                 this.particleSystem.emissionRate = 50 * lostHealth / TankHealth.maxHealth;
21             }
22             lastHealth = tankHealthScript.health;
23         }
24     }
25 }

原版坦克舰队里的冒烟素材图片是这样的:

为了正常显示,需要设置其Shader为Mobile Particles/Alpha Blended,并设置Tiling和Offset如下图所示。

如果您不了解Tiling和Offset的含义,可以参考这里(图文详解Unity3D中Material的Tiling和Offset是怎么回事)。

+BIT祝威+悄悄在此留下版了个权的信息说:

如果您需要项目源码请通过下方二维码捐赠10元并留下您的联系方式。

ps:感谢上次捐赠的人,虽然只有1人。此文作为回馈,但愿您满意。

ps:我更换了博客皮肤,自我感觉比较良好。

时间: 2024-09-29 17:33:39

[Unity3D]自己动手重制坦克舰队ArmadaTank(2)从碰撞说起的相关文章

[Unity3D]自己动手重制坦克舰队ArmadaTank

[Unity3D]自己动手重制坦克舰队ArmadaTank 我玩过一款坦克游戏ArmadaTank(坦克舰队),如下图所示 几个月前我尝试用Unity3D重制这款游戏,已经可以玩起来了.下面是在PC上的重制版截图. 还有Android版的 重制版 重制版有这么几个重点. 拥有所有原版的模型 所有的模型(坦克.老巢.树.建筑.石头.奖励……)都与原版的模型完全相同.通过分析原版模型的数据,我写了一个格式转换器,把原版模型格式转换为通用的3DS格式.这样一来,这些模型就可以用到各种App里了. 好吧

固件开发环境验证试验,检验重制固件能否正常工作

上集介绍: 昨天已经把固件开发的环境Keil基本搞好了,而且利用CCD1随机附带的固件程序C文件加上Suite 3.4.7提供的INC和LIB,并对其中不契合的地方稍加修改,最后编译成功,获得了HEX文件,比CCD1附带的HEX文件小了一点点.(不契合原因:老的fx2regs.h文件中没有对端口ABCD(E不能按位访问)的每一位进行定义,所以CCD1的txmaster.c文件开头部分以"sbit PD0 = IOD ^ 0;"的形式对所使用的端口的位进行了定义.当更换为新版本Suite

Xamarin.Forms之UserDialogs 重制版本

在 forms 里面,目前使用比较多的弹出组件是 Acr.UserDialogs ,但是这个组件有些小问题,比如 loading .hide 会同时把 toast 给一起关掉,android 下的 toast 希望是 安卓原生的toast 样子,而不是 底部弹出一个横条(其实是 android 的 Snackbar),对于 ios 的toast 也希望类似android 的样子,但是 Acr.UserDialogs 里面 toast 是 snackbar 样式,这并不符合产品需求情况. GitH

重制和回流

在讨论重绘.回流之前.需要对的呈现流程有些了解,是怎么把html结合css等显到浏览器上的,下的流程图显了浏览器对的呈现的处理流程.可能不同的浏览器略微会有些不同.但基本上都是类似的.1. 浏览器把获取到的html代码解析成1个Dom树, html中的每个tag都是Dom树中的1个节点,根节点就是我们常?的document对象(<html> tag). dom树就是我们?firebug或者IE Developer Toolbar等?具看到的html结构,??包含了所有的html tag,包括d

数据库密码重制

1.编辑MySQL配置文件my.cnf 系统运维  www.osyunwei.com  温馨提醒:qihang01原创内容版权所有,转载请注明出处及原文链接 vi /etc/my.cnf    #编辑文件,找到[mysqld],在下面添加一行skip-grant-tables [mysqld] skip-grant-tables :wq!  #保存退出 service mysqld restart  #重启MySQL服务 2.进入MySQL控制台 mysql -uroot -p   #直接按回车

重制AdvanceWars第一步 -- 搞定地图

首先来聊下高级战争吧Advance Wars,由任天堂旗下的Intelligent Systems开发的战棋游戏.初作诞生于GBA上,后来继续跟进了高战2黑洞崛,而后在下一代掌机DS上也出了三代续作高战DS,以及后来不太一样的毁灭日.本人的高中时代正值GBA横行天下的时候,那时候最喜欢的事情就是晚上躲在宿舍厕所偷偷玩GBA(不熄灯).有时候还和小伙伴联机打GT2赛车马里奥等等,而高战...这种时间吃货能和小伙伴联机一把就实在太珍贵了.鉴于高战这种类型的硬核战棋在日本地区不受欢迎,估计以后是不会再

微软重制Windows 1.0系统:祖师爷出山了

Windows官方推特在7月1日发布了一条很有趣的动态,“向大家介绍全新的Windows 1.0,带MS-DOS.时钟等”.配发的视频回顾了从Windows 1.0/3.1到Windows 10期间,视窗LOGO的演变. 评论互动中,Windows官方显得一本正经的样子,说为何么不可以推出Windows 1.01. 后续的图片预热中,还出现了盒装Windows.盒装<微软飞行模拟>.盒装<Word>等.微软突然预热Windows 1.0系统:套路满满引发猜测 有些人说这是微软推迟3

unity3d中的trigger和collision消息以及刚体与碰撞体

一直困惑于unity3d中的触发和碰撞消息在什么条件下能够发生,平时用时也是一知半解.磨刀不误砍柴工,是时候发点时间一劳永逸的解决这个问题了XD. OnTriggerEnter, OnTriggerStay, OnTriggerExit 是为触发类消息,记为trigger OnCollisionEnter, OnCollisionStay, OnCollisionExit是为碰撞类消息, 记为collision None表示两类消息都没发生 如果对象有刚体(rigidbody)且其 IsKine

图文详解Unity3D中Material的Tiling和Offset是怎么回事

图文详解Unity3D中Material的Tiling和Offset是怎么回事 Tiling和Offset概述 Tiling表示UV坐标的缩放倍数,Offset表示UV坐标的起始位置. 这样说当然是隔靴搔痒. 下面用*.3ds文件作为模型,介绍Tiling和Offset到底是怎么回事. 3DS格式解析 比如我有这样一个tank_player.3ds模型.右侧的'select'处的图片就是贴图. *.3ds文件最基本的内容包括顶点列表Vertices.贴图坐标列表UVs.面列表Faces.其中Ve