![]() ![]() Let’s build a couple of actions for patrolling waypoints and shooting a projectile at the player. We have a couple properties for our enemy but it doesn’t do anything yet. Public class EnemyStats : ScriptableObjectīecause this is ScriptableObject, we can turn this script into a Unity asset and later map it to our state controller. At the moment I only care that the enemy have a moveSpeed and a turnSpeed. We will start with the most straight forward piece of the puzzle, the Data. Let’s build an enemy that can patrol some waypoints and shoot at the player. Now that we have some definitions and a framework in which to start crafting together an enemy, let’s try to build a simple enemy. A transition is simply how we get from “a” to “b”. ![]() Once we have made a decision we got to have some way of transitioning to the next action. We will see how to implement couple of decisions like PlayerDetected. Maybe something is on a timer, or maybe it’s an interaction with the player can changes our behavior. Somehow we have to decide when to change states and start doing some other action. For instance, the enemy is in the Patrolling state or the Chasing state. States are well… states! Think of states as an one or two word “present tense” description of the actions of the enemy. Later on, we will see how to define these actions. This is where most of your implementation and time spent will be held. ActionsĪctions are essentially the “verbs” of the enemy. These are all descriptive properties of what the enemy is composed of. To keep things clean we will hold all the properties of a enemies in a straight up data class called EnemyStats. We are going to think about an enemy in 5 different contexts, Data, Actions, States, Decisions, and Transitions. To do this let’s think about the core of what components make an enemy. The best solution involves a pattern in which you can use pluggable components to turn your state controller into simple building blocks that are simple, modular, testable, flexible, clean, and composable. Hint: Beer and code work well together when they are modular events. Plus, what if we have a several enemies that exhibit some of the same behaviors? It sure would be nice to not rewrite or copy/paste that code all over and balloon up our code base. Still, this is a lot of code that we have to consider and finding your issue or tweaking a behavior can be more tedious than it has to be. We have some separation now and it’s more clear of what’s happening with our state controller. (0f, 0f, Random.Range(-SpreadOfShots, SpreadOfShots)) īetter. GameObject shotObject = (GameObject)Instantiate(ShotPrefab, transform.position + transform.right * 0.5f, transform.rotation) If (Vector3.Angle(transform.right, WaypointPositions - transform.localPosition) _timeOfLastShot + TimeBetweenShots) While (Vector3.Distance(transform.position, WaypointPositions) > 0.1f) You will have a much easier time understanding and maintaining these smaller methods especially when you organize them into some logical order. Hint: Best results occur before Mom has to tell you twice!Ī better alternative would be to spilt up behaviors into separate coroutines which are IEnumerators running within the game loop. Maybe you can get by with this in a game jam or some small project but in a large scale project you will feel pain. ![]() Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical) ![]() (0f, 0f, Random.Range(-spreadOfShots, spreadOfShots)) ĭ("Traveling towards the waypoint") Ĭ = Vector3.MoveTowards(, destination,Ĭ * ltaTime) ĭ("Incrementing the waypoint") Ĭontroller.nextWayPoint = (controller.nextWayPoint + 1) % GameObject shotObject = (GameObject)Instantiate(shotPrefab, transform.position + transform.right * 0.5f, transform.rotation) if the player is detected shot at the playerĭ("Shoot the player.") While (Vector3.Distance(, destination) > 0.1f) As you can probably imagine this approach while maybe not to challenging to implement, it will be very difficult to maintain and understand. One approach to controlling a enemy behavior is by defining when to move, how to move, when to shoot, how to shoot, etc. Just Put It In Updateĭefining behaviors for entities in Unity is typically done with a StateController that implements Unity’s MonoBehavior class. Taking the time to break apart your state machine and crafting pluggable components will make your system testable, flexible, clean, and composable.ĭisclaimer: While I’m going to provide examples, not all the implementation details will be explained as this post is meant to exemplify the high level concept of building pluggable AI. State controllers in Unity can quickly get out of hand if you throw the kitchen sink into them. ![]()
0 Comments
Leave a Reply. |
AuthorWrite something about yourself. No need to be fancy, just an overview. ArchivesCategories |