Jump to content
  • Advertisement
  • 08/25/17 10:05 AM

    Enemy Design in Retro Games - Build a Bad Guy Work Shop

    Game Design and Theory

    Garmy
    • Posted By Garmy

    Enemies sell the levels

    Level design is more than the geometry of the levels. Blocks and power ups and traps all test the player's mettle, but it's the enemies that bring a level to life. Their personality and how well they fit the theme of the level is important for lore, but it's their behavior that makes the game play. Enemies that are varied and challenge the player in unique ways make for memorable experiences, especially when used in harmony with the level's geometry. 

    An example of a memorable enemy
    Brilliant enemy design, brilliant level placement! (source: A Critical Look at Mega Man 3 Stages)

    This article won't go into how and where to place enemies. Instead, it will focus on designing the enemy's behaviors. Just keep in mind that their placement and how they compliment the player's move and attack sets matter. Check out the super fun Super Mario Bros Crossover to see just how different enemy behavior can be when the player's character moves and attacks differently.

    Also, note that this article is mainly about designing enemies in a 2D space. Some of this will apply to 3D enemies, but the differences in dimensions and movement directions between the two play spaces are a lot larger than they might seem.

     

    Defining Enemy Behavior - States and Triggers

    After playing through virtually the entire NES and SNES library, I took notes on enemy behavior and ended up categorizing it all.

    Enemy behavior can be broken down into three main categories:

    • Movement - How they Move
    • Qualities - Their innate nature
    • Abilities - The things they can do

    By mixing and matching attributes of these three categories, you can create an Enemy State. Often, however, enemies have multiple states and switch through those states with "Triggers".

    I created lists of attributes for each of these qualities. Most interesting enemy design can be made by mixing and matching these attributes into unique states and then further deepening the experience with a series of triggers. Most common enemies have at most two states with a single trigger, but Boss fights are special in that they often have four or more states with a variety of triggers. Bosses that don't usually aren't very fun (see Axiom Verge for examples of this). 

    This list is very comprehensive and can describe the behavior of every enemy I could find in a retro game. I'm sure there are attributes and triggers I haven't listed, so if you know of any (especially Trigger ideas), leave a comment! Otherwise, this method can create every enemy from any top-down or side view action game you can think of.

    Also, consider projectiles (bullets, arrows, fireballs, etc) as enemies too. Some very cool projectiles can be created for both enemies and player character using the same rules!

    Without any further introduction, let's get to the attributes!

    Movement

    Stationary - The enemy does not move at all.

    Walker - The enemy walks or runs along the ground. Example: Super Mario's Goombas

    Riser - The enemy can increase its height (often, can rise from nothing). Examples: Super Mario's Piranha Plants and Castlevania's Mud Man

    Ducker - The enemy can reduce its height (including, melting into the floor). Example: Super Mario's Piranha Plants

    Faller - The enemy falls from the ceiling onto the ground. Usually, these enemies are drops of something, like acid. Some games have slimes that do this.

    Jumper - The enemy can bounce or jump. (some jump forward, some jump straight up and down). Examples: Donkey Kong's Springs, Super Mario 2's Tweeter, Super Mario 2's Ninji

    Floater - The enemy can float, fly, or levitate. Example: Castlevania's Bats

    Sticky - The enemy sticks to walls and ceilings. Example: Super Mario 2's Spark

    Waver - The enemy floats in a sine wave pattern. Example: Castlevania's Medusa Head

    Rotator - The enemy rotates around a fixed point. Sometimes, the fixed point moves, and can move according to any movement attribute in this list. Also, the rotation direction may change. Example: Super Mario 3's Rotodisc, These jetpack enemeis from Sunsoft's Batman (notice that the point which they rotate around is the player)

    Swinger - The enemy swings from a fixed point. Example: Castlevania's swinging blades

    Pacer - The enemy changes direction in response to a trigger (like reaching the edge of a platform). Example: Super Mario's Red Koopas

    Follower - The enemy follows the player (Often used in top-down games). Example: Zelda 3's Hard Hat Beetles

    Roamer - The enemy changes direction completely randomly. Example: Legend of Zelda's Octoroks

    Liner - The enemy moves in a straight line directly to a spot on the screen. Forgot to record the enemies I saw doing this, but usually they move from one spot to another in straight lines, sometimes randomly, other times, trying to 'slice' through the player.

    Teleporter - The enemy can teleport from one location to another. Example: Zelda's Wizrobes

    Dasher - The enemy dashes in a direction, faster than its normal movement speed. Example: Zelda's Rope Snakes

    Ponger - The enemy ignores gravity and physics, and bounces off walls in straight lines. Example: Zelda 2's "Bubbles"

    Geobound - The enemy is physically stuck to the geometry of the level, sometimes appears as level geometry. Examples: Megaman's Spikes, Super Mario's Piranha Plants, Castlevania's White Dragon

    Tethered - The enemy is tethered to the level's geometry by a chain or a rope. Example: Super Mario's Chain Chomps

    Swooper - A floating enemy that swoops down, often returning to its original position, but not always. Example: Castlevania's Bats, Super Mario's Swoopers, Super Mario 3's Angry Sun

    Mirror - The enemy mirrors the movement patterns of the player. Sometimes, the enemy moves in the opposite direction as the player. Example: Zelda 3's Goriya

     

    Qualities

    GeoIgnore - The enemy ignores the properties of the level's geometry and can pass through solid objects. Examples: Super Mario 3's Rotodisc, many flying enemies will ignore geometry as well.

    GeoDestroyer - The enemy can destroy blocks of thee level's geometry. Examples: Super Mario 3's Bowser, Bob-ombs.

    Shielder - Player attacks are nullified when hit from a specific direction or angle, or hit a specific spot on the enemy. Example: Zelda 2's Dark Nuts

    Reflector - Player's ranged attacks are reflected when the enemy is hit by them.

    Damager - The player takes damage when colliding with the enemy. Most enemies have this.

    Redamager - The player takes damage when striking the enemy. Example: Link to the Past's Buzz Blob

    Secret spot - The enemy has a specific spot where damage can only be taken when that spot is hit. The opposite of a Shielder. Many bosses have this attribute.

    Invulnerable - The enemy cannot be harmed or killed. Examples: Most are geometry based hazards (spikes, mashing blocks, fire spouts), but also includes enemies like Super Mario 3's Boo Brothers.

    Reanimator - The enemy is revived after dying. Examples: Castlevania's Skeletons, Super Mario's Dry Bones.

    Regenerator - The enemy's health is regenerated over time.

    Secret Weakness - The enemy is vulnerable to specific types of attacks (fire, magic, arrows, etc). Example: Zelda's Dodogno (weakness to bombs).

    Hard to Hit - These enemies are specifically hard to hit with by the player's normal attack ranges. They are often very fast, or very tiny. Example: Zelda 2's Myu

    Segmented - These enemies are made up of smaller individual objects or enemies. Each individual segment can often be destroyed independently, and are often in snake-form - but not always. Example: Zelda's Manhandla, Super Mario's Pokeys

    Bumper - These enemies act like pinball bumpers, pushing the player (or other objects) away quickly. Example: Zelda 3's Hard Hat Beetles

    GeoMimic - These enemies have properties of level geometry. Most commonly, this means the player can stand on them. Examples: Most enemies in Mario 2, Megaman X's Sigma, Castlevania's Catoblepas

    Alarm - An enemy or object that causes another enemy or object to react. I didn't record any specific examples, unfortunately.

    Meta-vulnerable - The enemy can be defeated by meta-actions the player takes such as resetting the game, purchasing an in-app item, sharing the game on social networks, playing the game upside down with an accelerometer/compass, playing the game in a well-lit room, making noise in a microphone, etc. Example: Japanese Zelda's Pol's Voice

    Abilities

    Melee Attack - The enemy has a close-range attack.

    Emitter - The enemy 'emits' another enemy or projectile. This type is very similar to the Thrower, and the differences are mostly semantic. The main difference being that throwers throw an object they've picked up or spawn with, while Emitters have an infinite supply of the things they can throw out. Examples: Super Mario 3's Bullet Bill Cannons, Super Mario's Lakitus, anything that has a gun, Gauntlet's enemy spawners.

    Thrower - The enemy can throw whatever it is holding (an object, the player, or another enemy). This is often combined with Carriers, but not always. Examples: Castlevania's Barrel throwing Skeletons, Super Mario 3's Buster Beetle

    Grower - The enemy can grow in size.

    Shrinker - The enemy can shrink in size.

    Forcer - The enemy can apply direct movement force to the player. I don't have any specific examples, but treadmills found in most games apply, as well as enemies who blow air at the player, causing them to move backward.

    Carrier - The enemy can carry another enemy, an object, or the player. This one is a bit broad but includes enemies that pick up the player, that 'eat' the player and spit it out again, or otherwise grab and restrict the movement of the player. Example: Zelda's WallMasters

    Splitter - The enemy can split into multiple other enemies while destroying itself. Examples: Rogue Legacy's Slimes, Legend of Zelda's Vire and Biri.

    Cloner - The enemy can duplicate itself. Often, the duplicate is an exact clone, but sometimes, the duplicate is very slightly modified (such as having reduced health or being faster). The enemy that does the cloning is not destroyed. Example: Zelda's Ghini

    Morpher - The enemy morphs into another enemy type. Sometimes, this is temporary, and sometimes, the enemy will morph into multiple other types of enemies. 

    Sapper - The enemy can cause the player's stats to be modified (slower speed, reduced or eliminated jump, inability to attack, etc). These effects can be either permanent or temporary.

    Latcher - Like a Sapper, the enemy can drain the player's stats and abilities but does so by latching on to the player. Example: Mario 3's Mini-Goombas, Zelda's Like-Likes.

    Hider - The enemy can hide from the player. Typically, the enemy is hidden until the player comes within a set distance or the inverse where the enemy hides once the player comes too close. Example: Mario's Piranha Plants.

    Exploder - The enemy can destroy itself and cause splash damage. Example: Proximity Mines found in many games, Mario's Bo-Bombs

    Interactor - The enemy can interact with scripted objects, such as levers or buttons.

    Charger - The enemy will pause its behavior between switching to another behavior example: Castlevania's Wolves (Who pause a moment and lunge towards the player)

    Meta-Manipulator - The enemy can manipulate meta objects, such as the time of the day, the weather, the player's gender, save files, or unlock game modes.

     

    Triggers

    Random - The enemy randomly switches to a new state.

    Timed - The enemy will switch to a new state after a certain period of time has passed.

    Time Cycle  - The enemy will switch states depending on the time of day. For example, sleeping during the night, active during the day.

    Distance Traveled - The enemy has traveled a certain distance

    Proximity - The enemy is within a certain distance of another object, usually the player, but can also be another enemy, an object in the level, or a projectile.

    Post-Ability - The enemy just finished using an ability. Example: The enemy shoots a bazooka, then hides behind the level geometry

    Occupies Volume - The enemy has entered a volume of space. This can be a volume like a water zone, or an invisible volume defining the boundaries of a town.

    Stat Value  - The enemy changes its behavior depending on its own stats or the stats of the player. Examples: The enemy's health drops below 10% and it explodes, or the player has over 500% magic points and the enemy tries to siphon it away.

    Line of Sight - The enemy can personally 'see' the player, another enemy, or an object.

    Scripted Event - A scripted event causes the enemy to change its behavior. 

    Someone Else - A trigger is invoked on another enemy. Example: One enemy falls below 20% health and all the other enemies rush at the player.

    Death  - The enemy runs out of health or otherwise dies.

     

    Variables

    Many of these behaviors and attributes can be modified to create even more variation by tweaking just a few variables. In fact, variables could be all that change when triggers are invoked. Here are some examples:

    • Movement Speed
    • Jump Height
    • Sine Wave Length and Amplitude
    • Attack Range
    • Splash Damage Radius
    • Enemy Size
    • Dash Distance
    • .. and so on

    Think about what values you plug into your behaviors and if it would be better to increase or decrease those values to make even more unique enemies. A fast moving Goomba is more of a threat than a slow one!

     

    Have Fun!

    This is a pretty fun list to mix and match. It's sort of like a Lego system for behavior! In fact, I built a neat little interactive app a while ago on my Portfolio that assists in this, even letting you make randomized states that can be used as a base. Feel free to play around with it, and above all, have fun! That's what game design is all about <3



      Report Article


    User Feedback


    Yeah, 2D enemies are heavily based on the idea of space. This is why you take damage when you collide with one, I think. It's to get you thinking spatially. I think every enemy I've ever fought in a 2D game required spatially aware strategies to overcome. On the flip side, very few 3D games outside of fighting games have managed to implement the use of space effectively. This is because, I think, the change in perspective. It's much easier to judge space when you've got a side-view, as you do in 2D.

    However it's very doable in full 3D with a behind-the-back camera. Dark Souls and Bloodborne pull it off magnificently. I can deliberately and consistently make enemy attacks miss me by what seems like a pixel, and then step in and punish them as I would in a 2D game or Street Fighter. I'm fully aware of the exact distance that each of my attacks cover, and I'm aware of the distance of each of the enemies attacks. The extremely precise movement that allows you to take microsteps helps, and you can generally get a pretty good estimate of an enemy's range before they even attack by looking at the weapon they're holding. 

    This isn't just me, either. If you've ever watched online PvP in Souls, you'll see the players constantly dancing around and strafing round each other for extended periods without attacking, and it looks odd and random to the uninitiated, but actually those players are jockeying for position, and trying to out-space each other. 

    It sounds like such a small thing, but it adds tremendously to the games that have it, IMO. This is why people find the Souls games so hard to start out with, I reckon. It's because they ask you to think spatially, and most games just don't ask that of you anymore. Anyways, that's one thing that I enjoy about 2D games, and I'd love to see more of it in 3D.

    Share this comment


    Link to comment
    Share on other sites


    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

  • Advertisement
  • Game Developer Survey

    completed-task.png

    We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a $15 incentive for your time and insights. Click here to start!

    Take me to the survey!

  • Advertisement
  • Latest Featured Articles

  • Featured Blogs

  • Advertisement
  • Popular Now

  • Similar Content

    • By mujina
      What could be a way of avoiding using inheritance and virtual methods when designing components for an entity-component-system?
      I'll be more specific about my design issue:
      I currently have different classes for different kinds of colliders (let's say, CircleCollider and LineCollider).
      My system that checks for collisions and updates the positions and/or velocities of my entities should be something like:
      for entity_i in alive_entities { collider_i = get_collider_of_entity(entity_i) // components of same kind are stored contiguously in separate arrays transform_i = get_transform_of_entity(entity_i) for entity_j in alive_entities { collider_j = get_collider_of_entity(entity_j) transform_j = get_transform_of_entity(entity_j) if check_collision(collider_i, collider_j) { update(transform_i) update(transform_j) } } } my problem is that I don't have a generic `get_collider_of_entity` function, but rather a function `get_circle_collider_of_entity` and a separate one `get_line_collider_of_entity`, and so on. (This happens because under the hood I am keeping a mapping (entity_id -> [transform_id, sprite_id, circle_collider_id, line_collider_id, ...]) that tells me whether an entity is using certain kinds of components and which are the indices of those components in the arrays containing the actual components instances. As you can see, each component class is corresponding to a unique index, namely the index position of the array of the mapping described above. For example, transforms are 0, sprites are 1, circle colliders are 2, line colliders are 3, and so on.)
      I am in need to write a system as the one in the snippet above. I can write several overloaded `check_collision` functions that implement the logic for collision detection between different kinds of geometric primitives, but my problem is that I am not sure how to obtain a generic `get_collider_of_entity` function. I would need something that would get me the collider of an entity, regardless of whether the entity has a circle collider, a line collider, a square collider, etc.
      One solution could be to write a function that checks whether in my internal entity_id -> [components_ids] mapping a certain entity has a collider at any of the indices that correspond to colliders. For example, say that the indices related to the collider classes are indices 10 to 20, then my function would do
      get_collider_of_entity (entity_id) { for comp_type_id in 10..20{ if mapping[entity_id][comp_type_id] not null { return components_arrays[comp_type_id][entity_id] } } return null } This could turn out to be pretty slow, since I have to do a small search for every collider of every entity. Also, it may not be straightforward to handle returned types here. (I'm working with C++, and the first solution - that is not involving inheritance in any way - would be returning a std::variant<CircleCollider, LineCollider, ... all kinds of components>, since I would need to return something that could be of different types).
      Another solution could be having some inheritance among components, e.g. all specific component classes inherit from a base Collider, and overrride some virtual `collide_with(const Collider& other)` method. Then I would redesign my mapping to probably reserve just one index for colliders, and then I would actual colliders in a polymorphic array of pointers to colliders, instead of having a separate array for CircleColliders, another for LineColliders, and so on. But this would destroy any attempt to be cache-friendly in my design, wouldn't it? That's why I am looking for alternatives.
      A third alternative would be to just have a single, only, Collider class. That would internally store the "actual type" ( aka what kind of collider it is ) with dynamic information (like an enum ColliderType). Then I would have all colliders have all members needed by any kind of colliders, and specific collision detection functions which I can dispatch dynamically that only use some of that data. (Practical example: a "Collider" would have a radius, and the coordinate for 2 points, and in case its type was "circle" it would only make use of the radius and of one of the 2 points - used as the center -, while if it was a "segment" it would only make use of the 2 points). My gut feeling is that this would bloat all colliders, and, even if the bloat could be reduced - using unions in some smart way for storing members? I wouldn't know how -, then still the design would be pretty brittle.
      I'm clueless and open for ideas and advice! How do you handle in general situations in which you have components that can be naturally modeled as subclasses of a more generic component class? Inheritance? Smart hacks with variants, templates, macros, custom indexing? Dynamic "internal" type?
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!