纳金网

标题: 第一人称射击游戏(中级教程)(四) [打印本页]

作者: 会飞的鱼    时间: 2011-12-13 14:22
标题: 第一人称射击游戏(中级教程)(四)
生命值(Hit Points)

脚本Explosion和MachineGun已经展示了当物体被击中时(火箭弹或子弹),如何设定毁伤程度,并把伤害值传给周围的游戏物体。但是,游戏物体并不知道这些值意味着什么。

游戏物体的健康程度可以通过使用变量hitPoints来进行跟踪。每个物体都有自己的生命值(根据物体自身的生命强度)。每个游戏物体也应该对伤害有所反应并使用了ApplyDamage()函数(注意,这正是Explosion和MachineGun脚本中调用的用来产生伤害函数)。这个函数将会根据伤害程度降低游戏物体的生命值,并在生命值为零时调用其它函数来产生一定的变化(例如死亡或爆炸)。

下一节我们来看看如何使用生命值(hitPoints)和函数ApplyDamage()
爆炸的桶子(Exploding Barrels)

下面的脚本是通用脚本,所以可以作为一个部件加载在任何可以产生毁伤的物体上。DamageReceiver脚本代码如下:
var hitPoints = 100.0;

var detonationDelay = 0.0;

var explosion : Transform;

var deadReplacement : Rigidbody;

function ApplyDamage (damage : float)

{

// We already have less than 0 hitpoints, maybe we got killed already?

if (hitPoints <= 0.0)

return;

hitPoints -= damage;

if (hitPoints <= 0.0)

{

// Start emitting particles

var emitter : ParticleEmitter = GetComponentInChildren(ParticleEmitter);

if (emitter)

emitter.emit = ***e;

Invoke("DelayedDetonate", detonationDelay);

}

}
函数对被击中或被爆炸影响的物体应用毁伤效果。如果物体生命值已经是0或者更少,不会有任何效果,否则生命数值会根据毁伤效果不断减小。当生命值比零还小的时候就会调用DelayedDetonate函数(延迟只是为了爆炸效果更酷,没别的原因)。
function DelayedDetonate ()

{

BroadcastMessage ("Detonate");

}
这就是引爆游戏物体和其子物体的方法
function Detonate ()

{

// Destroy ourselves

Destroy(gameObject);

// Create the explosion

if (explosion)

Instantiate (explosion, transform.position, transform.rotation);
如果桶子上加载了爆炸预制,那么生命值为0是就会显示这个爆炸预制。
// If we have a dead barrel then replace ourselves with it!

if (deadReplacement)

{

var dead : Rigidbody = Instantiate(deadReplacement, transform.position,

transform.rotation);

// For better effect we assign the same velocity to the exploded barrel

dead.rigidbody.velocity = rigidbody.velocity;

dead.angularVelocity = rigidbody.angularVelocity;

}
如果游戏物体还有一个死亡状态,那么桶子爆炸,然后用这种死亡状态代替这个游戏物体。同时要注意方向的一致性。
// If there is a particle emitter stop emitting and detach so it doesnt get destroyed

// right away

var emitter : ParticleEmitter = GetComponentInChildren(ParticleEmitter);

if (emitter)

{

emitter.emit = false;

emitter.transform.parent = null;

}

}

// We require the barrel to be a rigidbody, so that it can do nice physics

@script RequireComponent (Rigidbody)
下面我们在几个桶子上使用DamageReceiver脚本。先插入一些资源:

拖放Objects/crateAndBarrel/barrel物体到场景中

对桶子(barrel)添加刚体属性

给桶子增加一个盒子碰撞器(box collider)。根据桶子大小调节盒子碰撞器大小。在盒子碰撞器参数栏中的Size中调节。

层级栏中,把脚本DamageReceiver付给桶子

把标准资源库中的explosion预制付给Explosion变量(在桶子的毁伤接收器属性中)

创建一个新预制Barrel(注意大写B,插入的模型桶子名字是小写b)

把设置好的桶子物体(barrel)从层级栏中拖放到工程栏中的新建的预制上

增加几个Barrel到场景(直接复制即可)

运行游戏



你可能注意到,当桶子爆炸后,它仅仅就只消失了,我们还需要添加一个爆炸后的桶子的来代替它。

创建一个新预制Barrel-dead

把原始桶子物体付给预制(从文件夹Objects/crateAndBarrel中导入的)

此时,桶子(Barrel)和爆炸后的桶子(Barrel-dead)看上去是一样的,因为他们的纹理一样(barrel1)

我们需要炸后的桶子纹理跟桶子纹理不一样,这样玩家才知道爆炸没爆炸,类似烧毁的纹理。仅仅调节barrel1是不行的,必须创建一个新的材质付给Barrel-dead。首先复制材质barrel1,调节成烧毁的样子。

工程栏中选择barrel1材质(点击Barrel-dead,在参数Mesh Renderer中点 Materials,然后工程栏中会高亮显示barrel1)

复制材质barrel1(Ctrl+D),重命名为barrelDead。注意,复制的材质会自动命名为barrel2

设定材质,在参数Main Color中把颜色调为黑色

工程栏中选择Barrel-dead,材质栏中下拉菜单选择barrelDead

比较两个材质的不同

下一步,点选Barrel-dead,为其增加盒子碰撞器和刚体属性

把Barrel-dead配置到Barrel的Dead Replacement参数中

运行游戏,将会出现桶子爆炸前后不一样的效果
防卫机枪(Sentry Gun)

最后我们在游戏中增加一个敌人,一挺防卫机枪。防卫机枪将自动寻找玩家,并进行射击。

首先插入机枪模型:

把Object/weapons文件夹下的sentryGun物体拖放到场景中

给机枪增加box collider和rigidbody属性

调节下盒子碰撞器大小,使他包裹炮塔上机枪管

把脚本DamageReceiver付给机枪

现在我们来解释下防卫机枪的脚本代码。全代码如下SentryGun:
var attackRange = 30.0;

var shootAngleDistance = 10.0;

var target : Transform;

function Start () {

if (target == null && GameObject.FindWithTag("layer"))

target = GameObject.FindWithTag("layer").transform;

}
函数Start检测是否给机枪赋予了一个目标物体(参数面板中可以设置),但在参数面板中把Player标签付给参数会更方便(马上来操作)。
function Update () {

if (target == null)

return;

if (!CanSeeTarget ())

return;

// Rotate towards target

var targetPoint = target.position;

var targetRotation = Quaternion.LookRotation (targetPoint - transform.position,

Vector3.up);

transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation,

Time.deltaTime * 2.0);
如果玩家在游戏中,并被防卫机枪发现,机枪炮塔将从当前角度旋转朝向玩家。
// If we are almost rotated towards target - fire one clip of ammo

var forward = transform.TransformDirection(Vector3.forward);

var targetDir = target.position - transform.position;

if (Vector3.Angle(forward, targetDir) < shootAngleDistance)

SendMessage("Fire");

}
如果当前机枪管位置与玩家的夹角小于shootAngleDistance所设置的数值,机枪将开始开火。
function CanSeeTarget () : boolean

{

if (Vector3.Distance(transform.position, target.position) > attackRange)

return false;

var hit : RaycastHit;

if (Physics.Linecast (transform.position, target.position, hit))

return hit.transform == target;

return false;

}
函数CanSeeTarget计算出机枪是否看见了目标(在这里是玩家)
最后我们来完成对防卫机枪最后的设置:

设置防卫机枪的目标。层级面板中点选FPS控制器,标签栏中的下拉菜单选择标签Player。

把脚本SentryGun配置给防卫机枪的子物体sentryGunRotateY。这会使防卫机枪仅仅顶部旋转,三角基座固定不动。

把爆炸预制配置给机枪中DamageReceiver属性中的爆炸栏

把防卫机枪配置到DamageReceiver属性中的Dead Replacement栏(也可以配置一个自己创建的物体作为破坏状态物体)

把脚本MachineGun付给sentryGunRotateY

在资源库中,把sentryGunTop的子物体muzzleflash配置给机枪属性中的Muzzle Flash变量。

层级栏中点选muzzleflash,把它的着色器类型改为Particles/Additive

运行游戏。现在可以朝油桶和机枪开火了
天空物体(Skybox)

给场景添加天空效果。

    选择Edit>Render Settings。把skyBoxTest拖放配置给Skybox材质。这样就创建了天空。



由 lsermao  发表
作者: 奇    时间: 2012-1-25 23:29
新词新语迎新年, 年味十足乃作年.

作者: 晃晃    时间: 2012-1-28 23:22
美酒令人回味,音乐让人陶醉,好书百读不悔,情意形影相随,节日问候最可贵。又到年终岁尾,愿你幸福健康作陪,笑得合不拢嘴,预祝春节快乐!

作者: 晃晃    时间: 2012-2-8 23:29
呵呵,很好,方便罗。

作者: C.R.CAN    时间: 2012-3-14 23:25
心中有爱,爱咋咋地

作者: 晃晃    时间: 2012-3-15 23:32
不错 非常经典  实用

作者: 彬彬    时间: 2012-3-16 18:59
  学习
作者: 奇    时间: 2012-3-20 23:19
楼主收集的可真全哦

作者: C.R.CAN    时间: 2012-4-11 23:32
很有心,部分已收录自用,谢谢

作者: markq    时间: 2012-4-12 22:47
不错 非常经典 实用
作者: 晃晃    时间: 2012-4-14 23:24
路过、路过、快到鸟,列位请继续...ing

作者: 菜刀吻电线    时间: 2012-6-10 23:19
无聊时可以刷屏幕 灌水 也可以试试 帖子的标题究竟可以写多长

作者: 晃晃    时间: 2012-7-12 23:19
很经典,很实用,学习了!

作者: 奇    时间: 2012-8-3 01:53
先垫一块,再说鸟

作者: C.R.CAN    时间: 2012-8-8 01:41
凡系斑竹滴话要听;凡系朋友滴帖要顶

作者: 奇    时间: 2012-12-9 23:18
你们都躲开,我来顶

作者: 菜刀吻电线    时间: 2013-3-3 23:29
楼主收集的可真全哦

作者: Jackie    时间: 2013-3-4 11:03
路過, 學習了, 感緊找齊整套的教學, 感謝~




欢迎光临 纳金网 (http://rs.narkii.com/club/) Powered by Discuz! X2.5