[Unity3D入门]分享一个自制的入门级游戏项目"坦克狙击手"
[Unity3D入门]分享一个自制的入门级游戏项目"坦克狙击手"
我在学Unity3D,TankSniper(坦克狙击手)这个项目是用来练手的。游戏玩法来自这里(http://www.4399.com/flash/127672_3.htm),虽然抄袭了人家的创意,不过我只用来练习(目前还很不成熟,离人家的境界相差很大),坦克、导弹、建筑模型来自网络,应该不会有版权问题吧。
由于模型和代码总共10M以上了,需要源代码和发布的Windows版、网页版程序的同学麻烦支付100元并留下你的邮箱~
到目前为止,用到的Unity3D知识有:地形Terrain,子物体gameObject,预制体Prefab,粒子系统Shuriken,刚体rigidbody,碰撞体collider,场景scene。
本文将非常简略,因为我也不知道该详写什么略写什么。有任何问题的话请留言,我会详细回复,并且根据情况加入正文。
需要step by step指导的同学,可以参考(http://pixelnest.io/tutorials/2d-game-unity/)。我就是从这篇文章开始学习Unity3D的。有了这个基础,看本文就没有什么问题了。
如何创建大量坦克
目前TankSniper里有4个坦克模型。如你所见,游戏中需要出现大量的坦克。在Unity3D中,我们不用new SomeTank()这种方式创建坦克,而是用Unity3D自带的Instantiate(prefab)方法创建坦克。其中的prefab就是预先设计订制的坦克模板,所以叫预制体。
创建预制体很简单,你只需
- 在Hierarchy中创建一个Cube。
- 把导入的坦克模型拖拽到Hierarchy面板。
- 调整Cube和坦克模型的position、rotation、Scale,使Cube恰好包住坦克模型,然后把坦克模型拖拽到Cube下,成为Cube的子物体。
- 把Cube拖拽到Project面板的Asset文件夹下(或Asset的子文件夹下)。
这样,一个以Cube为名称的预制体就做好了。以后你就可以在C#脚本中通过写
Instantiate(Cube);
这样的句子来创建坦克了。
一个小问题是,为什么要把坦克模型当做Cube的子物体?理由有2:首先,这样可以任意调整坦克模型的transform属性,而预制体整体的transform仍旧可以是0,0,0,这样方便使用;然后,用Cube严密包裹坦克模型后,Cube可以作为碰撞检测的边界,长方体之间的碰撞计算量比复杂的坦克模型要小得多。这是一种常用的做法。
爆炸效果和导弹尾焰
爆炸和尾焰都是用粒子系统做的,通过调整粒子系统的参数就可以实现,而且我没有用任何纹理图片。视觉效果虽然一般,不过目前这不是我要学的重点,暂时知足常乐一下好了。
导弹攻击坦克
实际上就是在碰撞事件OnCollisionEnter中写代码:在导弹的OnCollisionEnter事件中添加爆炸的粒子系统并销毁导弹;在坦克的OnCollisionEnter事件中减掉一定数值的HP值,若HP<=0了,就用Unity3D自带的Destroy()方法销毁坦克。
void OnCollisionEnter(Collision collision) { //当碰撞体与刚体与其他碰撞体或刚体接触时调用
// foreach (ContactPoint contact in collision.contacts) {
// Debug.Log(string.Format("{0}", contact.ToString()));
// Debug.DrawRay(contact.point, contact.normal, Color.white);
// }
// if (collision.relativeVelocity.magnitude > 2)
// audio.Play();
foreach (ContactPoint contact in collision.contacts) {
ExplosionEffectHelper.Instance.Explode(ExplosionEffectHelper.ExplosionEffect.MissileExplosion, contact.point);
SoundEffectHelper.Instance.MakeExplosionSound();
Destroy(this.gameObject);
break;
}
}
MissileScript.cs
void OnCollisionEnter(Collision collision) { //当碰撞体与刚体与其他碰撞体或刚体接触时调用
//foreach (ContactPoint contact in collision.contacts) {
// Debug.DrawRay(contact.point, contact.normal, Color.white);
//}
//if (collision.relativeVelocity.magnitude > 2)
// audio.Play();
var missileScript = collision.gameObject.GetComponent<MissileScript>();
if (missileScript != null) {
var power = missileScript.power;
this.Damage(power);
}
}
TankHealth.cs
最开始我用的是OnTriggerEnter事件。不过OnTriggerEnter无法获取导弹和坦克碰撞的准确位置,也就无法在最准确的位置释放爆炸效果,所以换成了OnCollisionEnter。
关于isTrigger与触发OnTriggerEnter、OnCollisionEnter之间的关系,可参考(http://www.cnblogs.com/infly123/p/3920393.html),本文不再详细说明。
发射导弹
导弹也要做成预制体。
我的设定是:鼠标左键按下时,在摄像机正下方距地面一定高度处发射导弹,导弹速度方向要指向点击到的三维场景中的坐标。这就要求从屏幕坐标转换到世界坐标。我愁了两天,终于找到了办法。
// 每帧调用一次,用于更新游戏场景和状态(和物理状态有关的更新应放在FixedUpdate里)
void Update () {
if (Input.GetMouseButtonDown()) { shooting = true; }
if (Input.GetMouseButtonUp()) { shooting = false; } if (shooting) {
elapsedInterval += Time.deltaTime;
if (elapsedInterval >= shootInterval) {
elapsedInterval = ;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//从摄像机发出到点击坐标的射线
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo)) {
/*
Debug.DrawLine(ray.origin, hitInfo.point);//划出射线,只有在scene视图中才能看到
GameObject gameObj = hitInfo.collider.gameObject;
Debug.Log(string.Format("Clicked object's name: {0}", gameObj.name));
*/
Transform missile = Instantiate(missilePrefab) as Transform;
Vector3 position = Camera.main.transform.position;
missile.position = new Vector3(position.x, position.y * / , position.z);
Vector3 dirPos = (hitInfo.point - missile.position);
dirPos.Normalize();
missile.gameObject.rigidbody.velocity = dirPos * ;
SoundEffectHelper.Instance.MakePlayerShotSound();
}
}
}
}
ShootMissile.cs
这时你会发现,导弹虽然按照要求的方向走了,但是全部是像螃蟹一样横着飞过去的。这不科学。所以要把导弹的旋转方向调整到飞行方向。这个问题我又琢磨了一天,找到了办法。
static readonly Vector3 missileInitialRotation = new Vector3(-, , );
// 每帧调用一次,用于更新游戏场景和状态(和物理状态有关的更新应放在FixedUpdate里)
void Update () {
if (Input.GetMouseButtonDown()) { shooting = true; }
if (Input.GetMouseButtonUp()) { shooting = false; } if (shooting) {
elapsedInterval += Time.deltaTime;
if (elapsedInterval >= shootInterval) {
elapsedInterval = ;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);//从摄像机发出到点击坐标的射线
RaycastHit hitInfo;
if (Physics.Raycast(ray, out hitInfo)) {
/*
Debug.DrawLine(ray.origin, hitInfo.point);//划出射线,只有在scene视图中才能看到
GameObject gameObj = hitInfo.collider.gameObject;
Debug.Log(string.Format("Clicked object's name: {0}", gameObj.name));
*/
Transform missile = Instantiate(missilePrefab) as Transform;
Vector3 position = Camera.main.transform.position;
missile.position = new Vector3(position.x, position.y * / , position.z);
Vector3 dirPos = (hitInfo.point - missile.position);
dirPos.Normalize();
missile.rotation = Quaternion.FromToRotation( //从螃蟹式到科学式,需要这样的旋转。
missileInitialRotation, //螃蟹式的旋转向量
dirPos); //科学式的旋转向量
missile.gameObject.rigidbody.velocity = dirPos * ;
SoundEffectHelper.Instance.MakePlayerShotSound();
}
}
}
}
ShootMissile.cs
目前的缺点
如你所见,有的坦克由于前后撞击加上地形起伏,竟然飞了起来。我已经用代码和物理属性调整过,但还是没有完全消除这种情况。
导弹尾焰和爆炸效果还不是很理想。
没有开始、存档、选项等菜单,没有我方HP、关卡、敌方剩余坦克数等信息。
敌方坦克还不会开炮。(欺负人。。。)
敌方坦克只知道向右(Z轴正方向)走,没有一点AI。
导弹只能攻击命中的坦克,对附近的坦克没有波及伤害。
总结
有了Unity3D,做游戏涉及的很多算法都不需要自己写了。Unity3D对提高生产效率的确有非常大的帮助。
需要源代码和发布的Windows版、网页版程序的同学麻烦支付100元并留下你的邮箱~
[Unity3D入门]分享一个自制的入门级游戏项目"坦克狙击手"的更多相关文章
- [Unity3D入门]入门级游戏项目"坦克狙击手"更新
[Unity3D入门]入门级游戏项目"坦克狙击手"更新 在上一篇中我分享了一个尚未完全写好的入门级unity3d项目"坦克狙击手". 本文介绍最新版的" ...
- 分享一个自制的 .net线程池
扯淡 由于项目需求,需要开发一些程序去爬取一些网站的信息,算是小爬虫程序吧.爬网页这东西是要经过网络传输,如果程序运行起来串行执行请求爬取,会很慢,我想没人会这样做.为了提高爬取效率,必须使用多线程并 ...
- 分享一个自制的USB转HART模块
HART协议是一种用于现场智能仪表和控制室设备之间的通讯协议.使用USB转HART模块可以很方便的对HART总线上的数据进行监控,并且可以远程控制.操作和校准HART设备.设计的模块主要采用的是USB ...
- Unity3D入门其实很简单
在上次发布拙作后,有不少童鞋询问本人如何学习Unity3D.本人自知作为一名刚入门的菜鸟,实在没有资格谈论这么高大上的话题,生怕误导了各位.不过思来想去,决定还是写一些自己的经验,如果能给想要入门U3 ...
- Unity3D入门之JavaScript动态创建对象
接着上一篇Unity3D入门文章,这里继续使用JavaScript脚本语言. 调试:Unity集成了MonoDevelop编辑器,在代码某行的左侧点击,即可下一个断点.然后先关闭Unity编辑器,在M ...
- Unity3D 入门 游戏开发 Unity3D portal game development
Unity3D 入门 游戏开发 Unity3D portal game development 作者:韩梦飞沙 Author:han_meng_fei_sha 邮箱:313134555@qq.com ...
- 【微信支付】分享一个失败的案例 跨域405(Method Not Allowed)问题 关于IM的一些思考与实践 基于WebSocketSharp 的IM 简单实现 【css3】旋转倒计时 【Html5】-- 塔台管制 H5情景意识 --飞机 谈谈转行
[微信支付]分享一个失败的案例 2018-06-04 08:24 by stoneniqiu, 2744 阅读, 29 评论, 收藏, 编辑 这个项目是去年做的,开始客户还在推广,几个月后发现服务器已 ...
- 分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间)
分享一个SQLSERVER脚本(计算数据库中各个表的数据量和每行记录所占用空间) 很多时候我们都需要计算数据库中各个表的数据量和每行记录所占用空间 这里共享一个脚本 CREATE TABLE #tab ...
- 分享一个MySQL分库分表备份脚本(原)
分享一个MySQL分库备份脚本(原) 开发思路: 1.路径:规定备份到什么位置,把路径(先判断是否存在,不存在创建一个目录)先定义好,我的路径:/mysql/backup,每个备份用压缩提升效率,带上 ...
随机推荐
- Maven中配置默认JDK版本
使用Maven构建项目时,默认是使用jdk1.3版本,目前很多使用泛型的项目肯定是无法通过编译,除了修改项目的pom文件外,还可以在Maven的配置文件settings.xml中添加如下配置来解决: ...
- Javascript日期比较
var date1=Date.parse("2014-9-3 11:40:14".replace(/-/g,"/")); var date2=Date.pars ...
- node学习笔记(二)
process.stdout(); //标准输出流 process.stdout.write() //提供了比console.log更底层的接口 process.stdin(); //标准输入流 // ...
- Java 第16章 封装
封装(encapsulation) 类使得数据和对数据的操作集成在一起,从而对使用该类的其他人来说,可以不管它的实现方法,而只管用它的功能,从而实现所谓的信息隐藏. 封装 , 使用类图描述类 ...
- git ignore
我最初将整个项目push到远程仓库,但是项目代码里面有大文件,从而传输太费时间了. 看网上的说法,可以通过ignore文件达到不提交某些文件的效果,尝试了一下发现不行. 后来尝试清除缓存 $ git ...
- iOS原生地图开发指南续——大头针与自定义标注
iOS原生地图开发指南续——大头针与自定义标注 出自:http://www.sxt.cn/info-6042-u-7372.html 在上一篇博客中http://my.oschina.net/u/23 ...
- 用Java程序判断是否是闰年
我们知道,(1)如果是整百的年份,能被400整除的,是闰年:(2)如果不是整百的年份,能被4整除的,也是闰年.每400年,有97个闰年.鉴于此,程序可以作以下设计: 第一步,判断年份是否被400整除, ...
- 基于 Winform + DotNetBar 写的股市行情助手
StockViewer 股市行情助手 简介 观看股市行情,窗口太显眼,是否担心被身后的老板发现? 窗口来回切换,工作时每隔几分钟就要看一眼股市.难道只能同时做一件事情吗? 现在,一款完全免费.开源的小 ...
- sharepoint学习。
企业门户:对全公司共用的信息进行统一管理.存储和发布,确保信息在公司范围内能够被及时传递 报表中心:集中管理.授权并发布所有业务报表,为各级管理人员提供各种数据.图形分析报表 办公协作:提供用户日常工 ...
- Drupal 7 安装时的数据库问题
在安装D7时,需要用PhpMyAdmin创建数据库,不建议使用ROOT帐号而需要建立一个新的帐号.一般,建立一个新的账号,如foo,并同时建一个同名的数据库,选择localhost(如果是本地).但是 ...