How to make a DOOM CLONE in Unity || E1M3 Enemy AI and NavMesh Part 1
Summary
TLDR本视频脚本介绍了在Unity中为游戏敌人添加视觉效果和行为反应的步骤。首先创建了击中效果的粒子系统,并将其设置为不循环播放,持续时间为半秒。接着,为敌人添加了音频效果,并在玩家射击时触发。然后,通过改变敌人材质来表示其警觉状态,并使用触发器和脚本来检测玩家的接近。最后,设置了基本的AI行为,使敌人在意识到玩家后能够追逐玩家。这个过程包括了设置导航网格、调整敌人的NavMesh Agent组件,并编写脚本来控制敌人的移动。视频还提到了在开发过程中遇到的一些技术问题以及解决方法。
Takeaways
- 🎮 在游戏中添加击中效果:创建新的粒子效果,设置持续时间为半秒,不循环,初始速度为10,颜色为深红色,使用重力1.5,模拟空间为世界空间,并通过触发器发射15个粒子。
- 🛠️ 将击中效果转换为预制体:创建预制体文件夹,将击中效果拖入文件夹,然后从层级结构中删除击中效果实例。
- 👾 敌人控制击中效果的生成:在敌人脚本中添加公共变量,用于生成击中效果,并在受到伤害时实例化该效果。
- 🔊 为枪添加临时音效:创建音频文件夹,添加枪声效果,调整音量,并在枪脚本中播放和停止音效。
- 🎨 敌人感知玩家时改变材质:创建新材质,添加触发器组件,当玩家进入触发器时,改变敌人的材质以显示其已经感知到玩家。
- 🔍 通过触发器和标签检测玩家:使用`CompareTag`方法和玩家标签来检测玩家是否进入敌人的触发区域。
- 🔧 玩家射击时使敌人感知:使用`OverlapSphere`方法检测射击范围内的敌人,并设置他们的感知状态。
- 🏃 设置导航网格代理:为敌人添加导航网格代理组件,设置速度,并在导航窗口中烘焙导航网格。
- 🤖 创建基础AI脚本:编写脚本使敌人在感知玩家后追逐玩家,使用`SetDestination`方法移动敌人至玩家位置。
- 🛑 解决敌人检测问题:修复由于敌人之间距离过近导致的检测错误,通过距离检查而非触发器来检测玩家。
- 🔄 持续迭代和改进:脚本和效果是临时的,将在后续进行重构和改进,以增强游戏的AI和测试体验。
Q & A
如何在游戏中为敌人添加命中效果?
-首先创建一个新的粒子效果,设置其持续时间为0.5秒,不循环播放。设置起始速度为10,起始颜色为深红色,并添加重力1.5。在发射器设置中,使用时间间隔为0的发射率,改为添加15个粒子的爆发。然后将这个粒子效果转为预制体,并放入预制体文件夹中。
为什么需要在敌人脚本中添加枪击效果?
-在敌人脚本中添加枪击效果是为了当敌人受到伤害时,能够在游戏中显示相应的视觉效果,增强玩家的游戏体验。
如何在游戏中实现枪声效果?
-首先创建一个新的音频文件夹并添加枪声效果文件。然后在玩家控制的角色上添加一个音频源组件,并设置音频剪辑为枪声效果。在枪的脚本中,使用GetComponent<AudioSource>()来获取音频源组件,并在射击方法中调用Play()函数播放枪声。
为什么需要在敌人模型上添加敌意材料?
-添加敌意材料是为了在敌人意识到玩家存在时,通过改变颜色来给玩家一个视觉提示,表明敌人已经进入警戒状态。
如何使敌人在玩家进入触发器时改变材料?
-在敌人脚本中使用OnTriggerEnter方法检测玩家是否进入触发器。如果是玩家,则获取敌人的MeshRenderer组件,并将其material属性设置为敌意材料。
为什么需要为敌人添加一个NavMesh Agent组件?
-NavMesh Agent组件负责在导航网格上导航,允许敌人根据玩家的位置移动,这是实现AI行为的基础。
如何设置敌人的导航网格?
-首先需要将地面和可行走的物体标记为Navigation Static,然后在Navigation窗口中进行烘焙,生成导航网格供敌人使用。
如何编写敌人AI脚本来追逐玩家?
-创建一个敌人AI脚本,在Start方法中初始化敌人意识和玩家位置的引用。在Update方法中,如果敌人意识到玩家,则使用NavMesh Agent的setDestination方法将目的地设置为玩家的位置。
为什么需要修改敌人检测玩家的方法?
-由于敌人之间的重叠和射线检测的问题,需要修改敌人检测玩家的方法以避免错误。可以通过距离检测和设置感知半径来实现。
如何在游戏中测试敌人的AI行为?
-在Unity编辑器中设置好所有脚本和组件后,可以通过播放测试来观察敌人是否能够根据玩家的位置和行为做出相应的反应,例如改变材料或追逐玩家。
为什么在测试中会出现控制台错误?
-控制台错误可能是因为射线检测时敌人的位置不正确导致的空引用错误。需要检查敌人的位置和射线检测的逻辑,确保敌人在正确的位置被检测到。
如何修复敌人之间的重叠问题?
-可以通过调整敌人的位置或大小,确保它们的碰撞体不会重叠。此外,也可以修改敌人检测玩家的逻辑,避免因为重叠而导致的错误。
Outlines
🎯 添加击中效果
我们从添加一个新的粒子效果开始,为敌人击中时添加视觉提示。创建一个新的粒子效果,命名为gun hit,设置持续时间为0.5秒,不循环,使用深红色。添加重力并设置为1.5。然后,在敌人的脚本中添加一个公用GameObject变量,将粒子效果拖入Inspector中。当敌人受到伤害时实例化该粒子效果。尽管这不是最优方法,但目前可以使用。之后,我们还将为枪添加音效。创建一个音频文件夹,将音效拖入,添加Audio Source组件,并在fire方法中播放音效。
🔊 增强敌人AI的感知能力
接着我们增强敌人的AI,使其能感知玩家。创建一个新的材质,并在敌人感知玩家时改变材质颜色。编写一个新的脚本Enemy Awareness,定义材质变量,并在OnTriggerEnter方法中检查是否为玩家,如果是则更改材质。然后,添加一个球形触发器,使敌人在玩家进入时变得警觉。接着在枪的脚本中,使用Physics.OverlapSphere方法模拟枪声半径,并使该范围内的敌人变得警觉。
🚶 设置敌人导航
设置敌人的导航行为。首先配置导航网格,使地面和大立方体可行走。然后在敌人上添加NavMesh Agent组件,调整速度。在Navigation窗口中烘焙导航网格,使NavMesh Agent可以在蓝色区域内导航。编写脚本使敌人追踪玩家,在start方法中获取Enemy Awareness组件和玩家的Transform。在Update方法中,若敌人感知到玩家,则设置目标位置为玩家位置。
📏 重新配置玩家检测方法
修正玩家检测方法以避免重叠的敌人检测错误。移除敌人的球形触发器,使用距离检查替代。在Enemy Awareness脚本中,定义一个玩家Transform变量,并在Start方法中初始化。使用Vector3.Distance方法计算敌人和玩家之间的距离,如果距离小于感知半径,则设置敌人为警觉状态。删除原有的OnTrigger方法并测试新方法。最终确认所有系统协同工作良好。
Mindmap
Keywords
💡粒子效果
💡Prefab
💡脚本编程
💡触发器(Trigger)
💡材质(Material)
💡音频效果
💡导航网格(Navmesh)
💡AI行为
💡射线检测(Raycasting)
💡层级(Layer)
Highlights
创建新的粒子效果来增强敌人受击时的视觉效果
设置粒子效果的持续时间、循环和生命周期
调整粒子效果的起始速度和颜色以符合游戏风格
使用重力和世界空间模拟粒子效果
创建预制体来简化游戏对象的复用
编写脚本控制敌人在受击时生成粒子效果
为枪械添加临时的音效效果
使用AudioSource组件来播放枪声
在脚本中控制音频的播放和停止
通过改变敌人材质来表示其警觉状态
创建敌人警觉性的新脚本和方法
使用触发器和标签来检测玩家接近
调整敌人AI使其能够追踪并追逐玩家
设置NavMesh以实现敌人的路径导航
编写AI脚本来控制敌人在警觉状态下追逐玩家
解决脚本中的空引用错误并优化敌人检测
使用距离检查代替触发器来检测玩家
完成基本AI并计划在未来进行扩展
Transcripts
we left off with this bland enemy it
takes damage and dies but there's little
indication
so let's fix that before we move forward
we're going to start by adding a hit
effect
so we'll create a new particle effect
we're going to zero out its position
and we'll name this something like gun
hit we'll make the duration like half a
second
we'll untick looping
the lifetime will also be half a second
for start speed we'll go with 10
for start color we'll go with the dark
red
we want gravity so we'll use 1.5
simulation space is world then we'll
expand emission
we'll set its rate over time to zero and
instead we'll add a burst
of 15 particles or so
that all looks good we're going to turn
this into a prefab but first we'll make
a folder for a prefabs
then we'll just drag this gun hit inside
the prefab folder
and then we can go back to our hierarchy
and delete the gun hit so for now our
enemy is going to be in charge of
spawning the gun hit particle so we'll
go into our enemy script
and at the top we'll say public game
object
gun hit effect
and we're going to drag this in the
inspector onto our enemy script
and then down in here where we take
damage
we can say instantiate and this is going
to spawn it and what we want
spawn is our gun hit effect and we want
it to spawn at our
transform position
and quaternion.identity is just a fancy
way of saying no rotation
this is not a good method but it's going
to work for now you can see our hit
effects keep piling up in the hierarchy
and we'll be using a pulling system
later but for now this is fine
now we want to add audio to the gun and
just like the hit effect this is only
temporary and we'll be doing this a
better way later
so first we're going to make a new audio
folder and i found the actual doom gun
sound effect but any gun sound effect
will work so just drag it in
and then we'll go to our player
and then the gun and we'll add a new
component and this component is going to
be an
audio source once that's up here on the
audio clip we're going to click this
circle and we're going to select our
doom gun sound
so the only thing we need to do this is
untick
play on awake and reduce the audio
volume a bit
i usually use 0.1 to start
so we're going to go back into our gun
script and inside the fire method is
where we're going to play this audio
[Music]
and we're going to be lazy we're not
even going to cache these references
we're going to use
git components and we need a git
component of audio source
and we'll call the play function
and then right above this we're going to
call the stop function just to make sure
audios don't overlap
so now that we have these makeshift
effects it should make building out the
ai and testing more enjoyable
and don't worry if you chose to skip
this for now because we're going to be
reworking all this and the code still
works the same
we're going to start the enemy ai simple
by making it aware of the player
and since we just added some
visualizations we'll continue with that
tradition
and we're gonna make the enemy swap out
its materials when it becomes aware
so to do that first we'll start by
making a new material we'll just
duplicate the enemy material
we'll call this something like enemy
aggro we'll just change the color of it
so we can see it changing once we've
created a new material we can just go to
our enemy and we're going to add a new
component
and it's going to be a new script called
enemy awareness
so we'll just double click that to open
it and then we'll clean it up
and up at the top we'll define that
material we'll call it public material
and we'll call it aggro matte for aggro
material
then we'll need a ontriggerenter method
so this will return a collider called
other and then inside here we're just
going to check
if other dot transform dot compare tag
and this takes in a string and we're
just gonna call it player
and then if it is the player we want to
get its mesh renderer
and then we're going to access the dot
material property
and we're going to set that material to
our aggro material
now we can save and head into unity and
then on the enemy script
we can just drag in our new material
so we're using an on-trigger enter so
our enemy also needs a trigger
we're going to use a sphere collider for
our trigger and we'll do that by
checking that is triggered so whenever a
player enters this trigger the enemy is
going to become alert
so something around eight for a radius
seems fine for now
so we'll collapse this for now and then
we're going to drag it up here below the
rigid body with the other components
and then one last thing we need to go to
our player and make sure that the tag
is tagged as player because that's what
we're referencing in our enemy awareness
script and if we save and test
our enemies should change colors when we
get too close
and it does so let's add a little bit
more functionality to this
so we'll go back into our enemy
awareness script and we still wanted to
change material so we're just going to
cut this real quick
and instead we're going to say is aggro
equals true
so up at the top we'll define this bull
and it's going to be a public bull
because other scripts are going to need
reference to this
and then we're going to make an update
loop and then
instead of just setting our material
whenever the trigger gets triggered
we have this bull being set instead so
we'll just say
if is aggro then we want to
change our material and we'll just paste
this line
so it's still detecting the player like
before but now there's a public bull
that we can access
or modify so now let's make the player's
gunshot
make the enemy aware so we have a player
and we have our enemy
the enemy knows where the player is
because of a sphere clutter trigger
so similar to how our gun can shoot a
raycast and return what it hits
it can also do something called a
overlap sphere
which will return an array of all the
colliders inside the radius
so if these are all enemy colliders then
we can know that they have
a enemy aware script we can loop through
these and enable the public bull
setting the enemies inside the radius to
become aware or aggro
so back in the gun script if you go down
here in our fire method
and look this for each statement here is
where our enemy takes damage
and possibly could get killed and that
might cause some errors if we try to
alert it after the fact
so at the very beginning of our fire
statement we're going to simulate the
gunshot radius
and alert any enemy in earshot
so at the top i'll start by creating a
float for the radius
we'll call it gunshot radius now i'll
default it to the same
as range at 20 and then i'll fix these
variables up a bit by setting the fire
rate to 1 which it is in the inspector
and we'll move it down with next time to
fire then i'll move
gunshot radius up with the other gun
variables
and the layer mask is fine and the box
collider and enemy manager are both fine
down there
so
so back to the fire method we want to
use physics dot overlap sphere
this is going to take a origin that
we're going to use transform.position
for
it's going to take a radius that we're
going to use gunshot radius for
and then we're going to add a layer mass
to make sure
these are only enemies
so we'll need to go to the top and we're
going to create another layer mask like
we did before
only this time it's going to be called
enemy layer mask
okay so the physics overlap sphere takes
a array of colliders so we're gonna
say we want a clotter array and we're
gonna call this
enemy colliders
and back here we'll say enemy colliders
equals
what we had already topped out
so now that we got a collection of enemy
colliders we can loop through each one
and alert the ones that are inside the
overlap sphere
to do that we need a for each statement
and for the
var we're going to say enemy collider so
the
singular of our collider array
in enemy colliders and this is our
collider array
and then we're going to say for each one
we want to go
enemy collider dot get component and we
want the
enemy awareness and then we made that
public bull
so we'll access it now and we'll say dot
is aggro equals true
we can save and now in unity we need to
go to our enemy object
and we're going to add a new layer we're
going to call this layer enemy
then we'll click off and then back on
enemy and assign
the layer now back on our gun script we
have this
enemy layer mask and we're going to
select enemy
and this also messed up our raycast
layer mask before we were checking if it
was
anything but the gun so default worked
for that but now we have a new enemy
layer
so we need default and
enemy selected so that's the new
everything
butt gun now with those set up we should
test to see that the enemy becomes aware
when we shoot our gun
oops i killed him so let's try it again
so let's back out of his personal space
and hit the button and yes he becomes
aggro
so now let's get it where it actually
moves to get the enemy to do something
we're going to need your basic nav mesh
setup
first we need to set up where to walk
the ground seems like it should be
walkable
the cubes at least the big one can be
walkable
we can select the entire environment
game object and at the top of the
inspector you can tick this arrow next
to static
and we can define this as navigation
static
that'll get it all ready to be baked
into a nav mesh next we'll go into our
enemy and give this a navmesh agent
component
this is responsible for navigating the
mesh
it's got a few variables that you can
edit here or in script
and already know that i want to increase
the speed a bit so i'm going to put
perhaps 7 here now we can go to the
navigation window
and if you don't see this you can find
it in window ai
and navigation so you have this option
to bake
and also an agents tab detailing what
it's baking for
i could bake this and it'd be fine but
it might take a while because the ground
is so massive
so i'm going to go to the ground and i'm
going to decrease its scale
by half and back in the navigation
window i can
bake now and you'll see this blue area
up here
and this is the area that the navamesh
agents are allowed to traverse
and this can only be seen when you have
the navigation window open
so let's close it and now that we have
our basic nav mesh it'll give us
something to build our ai on top of
so now we'll just make a script to chase
the player once it's aware
so first i'm going to move this navmesh
agent back up here with
other components
and then i'm going to add a new
component and i'm going to call this
enemy ai and then we'll double click it
to open
so i'm going to just clean this up and
then at the top we're going to need a
enemy awareness variable and it's going
to be private because it's on this game
object
and then we're going to use a private
transform for the players transform
because that's what we're going to be
moving toward
let's make a start function so we can
assign all these variables
first off we can say enemy awareness
equals get component
enemy awareness and that's going to pull
it straight off of this game object
and then players transform is going to
be a little different we're going to use
find object of type and we're going to
pick something that we know only the
player has so we know that player move
can only be found on the player so we'll
set it to that
and then we'll grab the transform from
it so we know if the enemy is aware
and we know how to find our player's
position so now to set the enemy to
go to the player we need to access its
navamesh agent so at the top we'll just
say
we want another private navmesh agent
and we want this called
enemy nav mesh agent
so in the start we can say enemy nav
mesh agent equals
get component of type navmesh agent
now on the update since we have a
reference to this we can call a function
from it called
set destination
and we want to set the destination to
the player's transform.position
and we only want to do this if the enemy
is aware
so we're going to wrap this in an if
statement with enemyawareness.aggro
and if it is then we'll set the
destination to players transform
position
and if it's not then we'll set it to our
own position
so now it's time to save and test and
everything looks like it's going well
until i get these console errors i did
end up finding the problem and instead
of fixing it
off camera i figured i would do it now
so i kept pointing at a line in my gun
script where it checks for the direction
from the player to the enemy
i know that line of code's fine it's
just that the enemy wasn't there so it
was a null error
i ended up checking my enemy manager and
the list had way more enemies than what
i was looking at
so i started looking around the enemy
again and i realized the problem
the big old sphere collider that we use
to detect the player
is being hit with the ray that the
enemies are so close together
that they overlap so there's conditions
where you're inside of a collider
shooting at another collider
so we're gonna have to fix this by
changing the method that we detect our
player
first off we'll get rid of all the
enemies but one and we'll remove the
sphere collider
so back in our enemy awareness script we
know that our ontrigger function is not
going to work anymore so we need another
way to find our player
we're going to do it like we did before
and at the top we're going to create a
new
private transform called players
transform
and then our start function will say
players transform
equals find object of type and then once
again we know that player move is on our
player and then we'll get our transform
off of it
so we'll do this using a distance check
so we'll create a
var dist and we'll set this equal to
vector3.distance
and a is going to be transform position
and our b
is going to be our player's
transform.position
and now that we got the distance we can
say if
our dist is greater than
and then we need a new float so at the
top we'll say public
float and this is going to be awareness
radius
then back down here we'll say if
distance is less than awareness radius
then we can say is aggro to true
and go down here and delete the
ontrigger method and just like that
we've created another method to get our
status
so now we can save and test and see if
this method is going to work but first
let's default this awareness radius to
eight
and also make sure that it's set in the
inspector so then we can play test
and now it looks to me like all our
systems are playing well together
so this is it for the basic ai and we'll
be doing more to it later
so i hope to catch you in part two spawn
camp out
関連動画をさらに表示
How to make a DOOM CLONE in Unity || E1M2 Enemy Hit Detection
Unreal Engine 5 RPG Tutorial Series - #21: AI Detection and Chasing
How to make a DOOM CLONE in Unity || E1M6 Doors and Keys
Unreal Engine 5 RPG Tutorial Series - #20: AI Behavior Trees Patrolling
How to make a DOOM CLONE in Unity || E1M4 Player Health and Armor
How to make a DOOM CLONE in Unity || E1M1 First Person Player
5.0 / 5 (0 votes)