Unity3D开发实践:动作游戏《君临都市》案例剖析(赠送活动)

内容速读:

文/周尚宣本文选自机械工业出版社出版的《Unity3D动作游戏开发》一书的9.1节,略有改动。经出版社授权刊登于此。本节将针对多人组合技能以及人形通用动作的设计来进行剖析。通用动作方案设计本作中设有60名敌人,包括不同的流派、体型、身高等,如第16关的空手道角色或女主角的功夫等。在Unity引擎中,使用人形动画的功能可以解决这类需求。

文/周尚宣


本文选自机械工业出版社出版的《Unity3D动作游戏开发》一书的9.1节,略有改动。经出版社授权刊登于此。


Unity3D开发实践:动作游戏《君临都市》案例剖析(赠送活动)


君临都市是一款PS2末期推出的动作游戏,它沿袭了格斗游戏严谨的判定并以拳脚格斗作为其主要战斗模式。战斗中存在着大量的投技、拆投、组合技等,游戏中还设有部位破坏的独特概念,角色被分为上中下三段伤害区域,玩家不可一味地对其某一段进行攻击,从而增加战斗的策略性。本节将针对多人组合技能以及人形通用动作的设计来进行剖析。


通用动作方案设计


本作中设有60名敌人,包括不同的流派、体型、身高等,如第16关的空手道角色或女主角的功夫等。如此之多的角色动画是这一类游戏的典型问题之一,通常可以采用一套通用动画的多个不同形体的方式并借助通用骨骼去解决,即一个动画同时做瘦、中、胖三个版本,以匹配不同体型的敌人。在Unity引擎中,使用人形动画的功能可以解决这类需求。


继续观察本作会发现,一些流派使用的角色相对较少,且角色大都为中等体型,并且女性角色较少。所以进一步优化,在制作通用骨骼动画时对于使用固定流派的敌人可以做一套通用;而对于通用流派的敌人,建议依据身高、体型制作两套或以上通用动画即可。


组合攻击的再实现


本作中的组合攻击通常是指多个己方角色同时对敌人发动的特殊动画攻击,或者是依赖站位在特殊条件下主角一人对多人发动的特殊动画攻击,如图1所示。


Unity3D开发实践:动作游戏《君临都市》案例剖析(赠送活动)

一对三组合攻击示意图


这里以主角一对多组合攻击的情形进行脚本实现,这种情形的触发逻辑一般是当主角周边站有敌人时,以敌人的某种朝向、站姿的指定规则进行触发。考虑到其与技能系统还是有一些区别,并且较为依赖敌人朝向等信息,故这里单独作为一个模块制作。


先来看一下实现这个模块所需要的脚本结构关系,如图2所示。


Unity3D开发实践:动作游戏《君临都市》案例剖析(赠送活动)

组合攻击功能脚本的逻辑关系


在图2中,ComposeAttackController脚本中存放着不同的组合攻击类型,通过Update事件函数每帧更新当前可触发的组合攻击,并将信息存于索引字段中。上下文Compose- AttackContext结构的信息存放了组合攻击所需要角色的自身组件,如Animator、Transform等,可根据需求自行增加字段。TriggeredComposeSkill函数是在外部模块调用时触发并通过协程执行的。


(1)首先定义一些基础脚本。先来定义上下文结构,它包含了角色自身的一些信息。


public struct ComposeAttackContext

{

public Transform CasterTransform { get; set; } //自身变换

public Animator Animator { get; set; } //自身Animator组件

}

随后编写ComposeAttackBase脚本,它定义了组合攻击的基本抽象行为。

public abstract class ComposeAttackBase : ScriptableObject

{

public abstract bool CanTrigger(ComposeAttackContext context, bool

prepareTrigger);

public abstract IEnumerator Trigger(ComposeAttackContext context);


CanTrigger函数判断当前是否可以触发组合攻击;第二个参数prepareTrigger决定是否记录参数以准备触发组合攻击,例如在检测的同时记录下RaycastHit信息。第二个函数Trigger将进入触发逻辑。


(2)接下来编写ComposeAttackController脚本,用于处理组合攻击逻辑,是该模块的核心脚本。


public class ComposeAttackController : MonoBehaviour

{

[SerializeField] Animator animator = null; //上下文所需接口,面板暴露参数

//组件列表面板暴露参数

[SerializeField] ComposeAttackBase[] composeAttackArray = null;

//当前已触发的组合技能索引

public int TriggerableComposeAttackIndex { get; private set; }

//对外提供组合技能数组列表

public ComposeAttackBase[] GetComposeAttackArray()

{

return composeAttackArray;

}

//每一帧更新组合技能是否触发逻辑,但可修改enabled关闭脚本更新

public void Update()

{

var context = new ComposeAttackContext() { Animator = animator,

CasterTransform = transform };

for (int i = 0; i < composeAttackArray.Length; i++)

{

var item = composeAttackArray[i];

if (item.CanTrigger(context, true)) //触发条件检测

{

TriggerableComposeAttackIndex = i;

break;

}

}

}

public IEnumerator TriggeredComposeSkill(int index)//组合技能的触发接口

{

if (index > composeAttackArray.Length - 1) //索引越界报错

throw new ArgumentOutOfRangeException();

var context = new ComposeAttackContext() { Animator = animator,

CasterTransform = transform };

yield return composeAttackArray[index].Trigger(context);//执行触发

}

}


通常将该脚本挂载至角色自身。


(3)接着编写一个具体组合攻击脚本ComposeAttack1。若角色前后或左右都有敌人,就会触发该组合攻击。


public class ComposeAttackController : MonoBehaviour

{

[SerializeField] Animator animator = null; //上下文所需接口,面板暴露参数

//组件列表面板暴露参数

[SerializeField] ComposeAttackBase[] composeAttackArray = null;

//当前已触发的组合技能索引

public int TriggerableComposeAttackIndex { get; private set; }

//对外提供组合技能数组列表

public ComposeAttackBase[] GetComposeAttackArray()

{

return composeAttackArray;

}

//每一帧更新组合技能是否触发逻辑,但可修改enabled关闭脚本更新

public void Update()

{

var context = new ComposeAttackContext() { Animator = animator,

CasterTransform = transform };

for (int i = 0; i < composeAttackArray.Length; i++)

{

var item = composeAttackArray[i];

if (item.CanTrigger(context, true)) //触发条件检测

{

TriggerableComposeAttackIndex = i;

break;

}

}

}

public IEnumerator TriggeredComposeSkill(int index)//组合技能的触发接口

{

if (index > composeAttackArray.Length - 1) //索引越界报错

throw new ArgumentOutOfRangeException();

var context = new ComposeAttackContext() { Animator = animator,

CasterTransform = transform };

yield return composeAttackArray[index].Trigger(context);//执行触发

}

}


这里通过CheckBox接口检测四周是否有敌人,mIsForwardAndBackword变量存储是左右受敌状态还是前后受敌状态。Trigger函数中的处理这里较为简单,在实际项目中建议将具体技能逻辑置于其中。


(4)最后将其在Project面板中创建,并结合ComposeAttackController脚本将其挂载。当外部模块触发输入后调用触发接口以触发组合攻击,如图3所示。


Unity3D开发实践:动作游戏《君临都市》案例剖析(赠送活动)

组合攻击完成效果图

相关推荐

UNITY3D、Unreal、Cocos2DX...介绍「游戏引擎」江湖的几个大玩家

Cocos2DX可能很多区块链行业的朋友还不了解。UNITY 3DUnity是目前3D手游领域使用量最多的引擎。Unity的开发者数量优势和成本优势不容小觑。UnrealUnreal是Epic Games开发的一款游戏引擎,是在手游领域市场占有率仅次于Unity的引擎。虽然在市场占有率上落后,但是在...

开发一款游戏引擎很难吗?为什么国内好像没有自己的游戏引擎。

说到游戏引擎很多人肯定就会想到虚幻引擎,寒霜引擎,CryEngine,Unity 3D等等,游戏引擎似乎是一个非常有技术含量的东西,基本上比较知名的大型游戏公司都有自己的游戏引擎,比如AE,育碧,R星,暴雪等等。总之游戏引擎说起来简单但实际上却非常复杂工具集合体,为游戏开发者带来了极大的便利,那么为...

Epic虚幻引擎有多强?别再吹虚幻4了,老牌unity宝刀未老

而放眼当下,市场中主流的游戏引擎无外乎Epic旗下的虚幻系列,以及unity3D引擎。前段时间Epic的虚幻引擎5重磅推出,在游戏界引起了众多玩家和行业人士的高度重视。虚幻系列引擎固然在游戏界有着重要地位,但是老牌引擎Unity也不是空有其表。在当下手游圈子中,很多深受玩家喜爱的游戏都是由unity...

盒子游戏,游戏玩家专属个性阅读社区


©CopyRight 2010- 2020 BOXUU.COM Inc All Rights Reserved

鄂公网安备 35020302000061号- 鄂ICP备2020015574号-1