Jump to content
  • Advertisement
Tristan Richter

Open Source Writing an ECS Game Engine, Difficulty with Effects

Recommended Posts

I am trying to implement status effects in my game but I have not quite been able to get the hang of it. Do you have to make a large number of classes for different kinds of status effects? Or is it possible to do it with just 1, or a small number of classes?

Share this post


Link to post
Share on other sites
Advertisement

I would have a small number of classes , the hierarchy somewhat like this:

Effect (contains duration and name , maybe if the target has to be friendly, etc)

   - StatEffect ( contains a value and an enum(i.e strength/movement/etc))

   - IntervalEffect (contains a effect value , damage type(enum or class) , frequency)

   - DamageEffect ( contains a damage amount , damage type(enum or class)

 and etc.

Share this post


Link to post
Share on other sites

Well it helps with a data oriented model. It depends on what your stat effects are. I'm thinking more about in the rpg sense. So for instance, you have a stat that increases your strength by 100 , or your movement speed by 50 , etc. With your method, you would have to create a different class for each of these, but if you do a single base class you can do things like : 

http://www.gdcvault.com/play/1024580/Modify-Everything-Data-Driven-Dynamic

A simple data driven example would be like this:

enum class EStatType : uint8
{
 MaxHealth,
 Stamina,
 Strength,
 MovementSpeed
};

class CStatEffect : public CEffect
{
public:

   void Apply(Character* C)
   {
     switch(m_StatType)
     {
       ...
     }
  }

void Remove(Character* C)
{ 
   switch(m_StatType)
   {
     ....
  }
}

void Serialize(IArchive& Ar)
{
  Ar.Serialize("StatType",m_StatType);
  Ar.Serialize("Value", m_Value);
} 
 
private:
   EStatType  m_StatType;
   int        m_Value;

};

and then you can have data definition files like : 


"Max Health Effect" := 
  "StatType" := "MaxHealth",
  "Value" := 500
}

using Json or XML or etc.

Share this post


Link to post
Share on other sites

My game is vaguely ECS-ish in places. The way I handle this is to make each status effect its own kind of "component" that gets attached to characters. The "components" are just blobs of state that get processed homogeneously.  In some cases, a "status effect component" is as simple as a handle to a character in a vector. I just go through the status effect lists every frame updating the characters as necessary. As status effects are added to the characters, each character gets its own entry in those arrays.

There is no inheritance or polymorphism necessary and everything is batch-processed. I've found it helpful for some purposes to have a bitmask on the character indicating which status effects are active, but the actual status effect processing is done in the status effect systems, not by the characters.

This is an attempt to be "data-oriented" (as opposed to merely "data-driven", which is what AxeGuyWithAnAxe is describing). Data for the status effects either comes from weapon or character definitions or, if status effect data is going to be uniform across all characters, separate files with per-status effect data.

Edited by Oberon_Command

Share this post


Link to post
Share on other sites
// header
struct StatusKnockback
{
	void clear();
	void finish_knockback(TypedHandle characterHandle);
	void record(
		TypedHandle sourceCharacterHandle,
		TypedHandle targetCharacterHandle,
		float duration);

	void apply_all_knockback(
		float dt,
		CharacterSystem& characters,
		SpriteObjectSystem& sprites,
		MovementSystem& movement);

private:
	struct Datum
	{
		TypedHandle sourceCharacterHandle;
		TypedHandle targetCharacterHandle;
		float timeRemaining;
	};

	std::vector<Datum> Records;
};

// cpp file
// the other methods are pretty self explanitory, so I'll just show the implementation of apply_all_knockback
void StatusKnockback::apply_all_knockback(
	float dt,
	CharacterSystem& characters,
	SpriteObjectSystem& sprites,
	MovementSystem& movement)
{
	VectorAlgorithm::consume_if(Records, [&](Datum& record)
	{
		// only apply knockback if the source character is still alive to push
		if (const CharacterState* sourceCharacter = 
			characters.try_and_get_datum(record.sourceCharacterHandle))
		{
			if (CharacterState* targetCharacter = 
				characters.try_and_get_mutable_datum(
					record.targetCharacterHandle))
			{ 
				sf::Vector2f move = sourceCharacter->compute_facing_vector();
				float scale = KNOCKBACK_SPEED_SCALAR * 2.0f * 
					record.timeRemaining / KNOCKBACK_DURATION_SCALAR;

				movement.cancel_movement_for(record.targetCharacterHandle);
				movement.queue_involuntary_move(
					record.targetCharacterHandle,
					move.x * scale,
					move.y * scale);
			
				targetCharacter->disable_all_actions();
			targetCharacter->set_status_flag(CharacterState::Status::Knockback);
				targetCharacter->override_definition_for_status(
					sprites, 
					StatusEffectDefinition::Name::Knockback);

				float nextKnockback = record.timeRemaining - dt;
				if (nextKnockback > 0.0f)
				{
					record.timeRemaining = nextKnockback;
					return false;
				}
			}
		}
		return true;
	});
}

This could of course be somewhat improved (eg. by not requiring that the source character still be alive, and only storing the direction the knockback is going in), but the gist of it is this:

StatusKnockback::apply_all_knockback is called once per frame when all the other status effects are updated. "Knockback" is the result of an attack where one character "pushes" another back a little ways.

VectorAlgorithm::consume_if takes an "updater" function and applies it to each element and then removes every element whose updater decided that it shouldn't be in the vector anymore. So we update each datum in the vector and remove the ones that have expired.

In case you've never seen it before: "datum" is the singular of "data."

Edited by Oberon_Command

Share this post


Link to post
Share on other sites

The code example works from the perspective of the Knockback effect rather than the character, so what happens is broadly:

  • each effect is stored inside StatusKnockback::Records
  • every update, the system goes through each of the stored effects and processes it
  • processing an effect involves looking up the 2 characters involved, applying all the changes to the target character, and tracking how much time is left
  • finally, the whole thing is wrapped in a tricky little structure that removes the effect if it returns true, so you don't have to loop over everything as a separate 'remove expired effects' step.

In terms of your original questions, this is basically a class per effect type, and the code example above is for the Knockback effect.

 

When I make status effect systems I like to brainstorm all the different types of effect and usually you can spot the commonalities that allow you to make just a small number of classes, or even just one. But how to do that depends entirely on the kinds of effects you need to implement.

 

Share this post


Link to post
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
  • Advertisement
  • Popular Tags

  • Similar Content

    • By JoshuaFraser
      Hi and thanks for reading, I have an issue with this reactive crosshair script, everything works fine until I start changing the offset. Give the script a go and you will see what I mean, when I do SetOffset(0f); it doesnt always set back to the origional state, if anyone can spot a fix I'd be super appreciative!
      using System.Collections; using System.Collections.Generic; using UnityEngine; public class ReactiveCrosshair : MonoBehaviour { [SerializeField] GameObject c_limb_prefab; private float center_offset = 0f; private float current_offset = 0f; private float max_offset = .5f; private int number_of_limbs = 4; private float limb_length = .05f; private float limb_width = .005f; private List<GameObject> c_limbs = new List<GameObject>(); public void SetupCrosshair(){ for (int i = 0; i < number_of_limbs; i++) { GameObject line_go = (GameObject)Instantiate (c_limb_prefab); line_go.transform.SetParent (this.transform); Vector3 limb_pos = new Vector3 (0f,0f,0f); //line_go.transform.position = limb_pos; line_go.transform.localPosition = limb_pos; LineRenderer line = line_go.GetComponent<LineRenderer>(); line.startWidth = limb_width; line.positionCount = 2; line.SetPosition (0, line_go.transform.localPosition + new Vector3(center_offset, 0f, 0f)); line.SetPosition (1, line_go.transform.localPosition + new Vector3(center_offset + limb_length, 0f, 0f)); line.useWorldSpace = false; c_limbs.Add(line_go.gameObject); } if (c_limbs != null) { OrientLimbs (); SetOffset (0f); } } public void OrientLimbs(){ for (int i = 0; i < c_limbs.Count; i++) { float rotation_step = 360f / (float)c_limbs.Count; c_limbs [i].transform.RotateAround (c_limbs[i].transform.position, c_limbs[i].transform.forward, 90f + (rotation_step * (float)i)); } } public void SetOffset(float _current_spread){ float offset = Mathf.Lerp (0f, max_offset, _current_spread); for (int i = 0; i < number_of_limbs; i++) { if (offset > current_offset) { Vector3 pos = c_limbs [i].transform.position + (c_limbs [i].transform.TransformDirection (Vector3.right) * offset); c_limbs [i].transform.position = pos; } if (offset < current_offset) { Vector3 pos = c_limbs [i].transform.position - (c_limbs [i].transform.TransformDirection (Vector3.right) * offset); c_limbs [i].transform.position = pos; } } Debug.Log ("SetOffset() offset: " + offset.ToString () + " _current_spread: " + _current_spread.ToString() + " localPos: " + c_limbs[1].transform.localPosition); current_offset = offset; } }  
    • By nilkun
      Hello everyone!
      First time posting in the forum.
      I've just completed my first game ever ( C++ / SDL ), and I am feeling utterly proud. It is a small game resembling Missile Command. The code is a mess, but it is my mess! In the process of making the game, I developed my own little game engine.
      My question is, where would be a good place to spread the news to at least get some people to try the game?
    • By Woody Stevens
      Hi,
       
      I am looking for a TCP or HTTP networking library similar to Lidgren (UDP).
       
      This is primarily for sending game map data and potentially other large messages from Server to Client.
       
      I do want to keep Lidgren for my chat messages, player position, small fast updates etc. I especially love the flow of data and the library usage in general, so any libraries of a similar style would be excellent. Preferably something open source, free and reliable.
      I also must be able to swap between localhost and an ip address with ease, like Lidgren, as I run a server for singleplayer/mp/lan.
       
      My game maps are similar to minecraft, but it is 2d and only one Z-level, so i'm sending a jagged array of Tile object data (currently only enum TileID.Grass) down the pipe to the Client. Problem is if i'm sending a large map 1024 x 1024 tiles down the to client that's quite a lot of data, and Lidgren is relatively slow to build the writes (before the message is even sent!). It is fine when i'm using smaller maps < 512 x 512 ( xTiles * yTiles ).

      I know about chunking and will look into implementing this later, whilst taking into account the user's position in the world to only send nearby chunks.
       
      An example of my code that can be slow:
      private void WriteWorld(NetOutgoingMessage outgoing) { try { var world = WorldManager.Instance.CurrentWorld; outgoing.Write(world.XTiles); outgoing.Write(world.YTiles); for (int x = 0; x < world.XTiles; x++) { for (int y = 0; y < world.YTiles; y++) { // Write Tile obj data outgoing.Write((int)world.Tiles[x][y]); // <-------- Slow here when xTiles and yTiles are each > 512 ! } } } catch (Exception ex) { // log send error } }  
      I'd love to hear from you guys, especially if any of you have come across a similar challenge.
    • By ethancodes
      I'm working on a system for my game that will allow the player to stack pick ups in a queue. As one pick up expires, the next automatically activates. I'm having an issue though where if I pick up the first one, it activates fine, but if i pick up a second directly after it, it overrides the first one, activates the second one, and then once it has run it's course, everything goes back to normal gameplay, no first pick up. I'm not sure why this is happening. Hopefully someone can spot what I'm doing wrong in my code.
      Here is the code for the pick up manager:
      // Update is called once per frame void Update () { if (pickUpQueue.Count != 0 && !pickUpActive) { pickUpActive = true; pickUpQueue[0].ActivatePickUp(); } DeactivatePickUp(); } void DeactivatePickUp () { if (pickUpQueue.Count != 0 && pickUpActive) { Destroy (pickUpQueue [0]); pickUpQueue.RemoveAt (0); pickUpActive = false; } } And here is the PickUp:
      public override void ActivatePickUp () { ball.GetComponent<Ball>().Speed = 2.0f; //increase ball speed... ball.GetComponent<Ball>().StartCoroutine(timer); //...set time that power up is active }  
      There is also a Base Pick Up:
      public void OnCollisionEnter2D (Collision2D collision) { Vector2 tweak = new Vector2 (Random.Range(0f, 0.2f),Random.Range(0f, 0.2f)); this.gameObject.GetComponent<Rigidbody2D>().velocity += tweak; //if the pickup makes contact with the paddle or ball.... if (collision.gameObject.tag == "Paddle" || collision.gameObject.tag == "Ball") { GameObject.FindObjectOfType<GameManager>().GetComponent<PickUpManager>().pickUpQueue.Add(this); Destroy(gameObject); //...and finally destroy power up object } } As a side note, I am trying to find a solution to this that will work for all of my pickups. Some pickups are ammo based, some are timed. 
    • By Hellados
      Hello guys, my name is Giorgi and i'm newbie game developer i'm learning Pixel art and after pixel art  i want learn C# and don't know how and where start i'm bad with programming language and know only HTML/CSS
    • By D34DPOOL
      Edit Your Profile D34DPOOL 0 Threads 0 Updates 0 Messages Network Mod DB GameFront Sign Out Add jobEdit jobDeleteC# Programmer for a Unity FPS at Anywhere   Programmers located Anywhere.
      Posted by D34DPOOL on May 20th, 2018
      Hello, my name is Mason, and I've been working on a Quake style arena shooter about destroying boxes on and off for about a year now. I have a proof of concept with all of the basic features, but as an artist with little programming skill I've reached the end of my abilities as a programmer haha. I need someone to help fix bugs, optomize code, and to implent new features into the game. As a programmer you will have creative freedom to suggest new features and modes to add into the game if you choose to, I'm usually very open to suggestions :).
      What is required:
      Skill using C#
      Experience with Unity
      Experience using UNET (since it is a multiplayer game), or the effort and ability to learn it
      Compensation:
      Since the game currently has no funding, we can split whatever revenue the game makes in the future. However if you would perfer I can create 2D and/or 3D assets for whatever you need in return for your time and work.
      It's a very open and chill enviornment, where you'll have relative creative freedom. I hope you are interested in joining the team, and have a good day!
       
      To apply email me at mangemason@yahoo.com
    • By Andrew Parkes
      I am a talented 2D/3D artist with 3 years animation working experience and a Degree in Illustration and Animation. I have won a world-wide art competition hosted by SFX magazine and am looking to develop a survival game. I have some knowledge of C sharp and have notes for a survival based game with flexible storyline and PVP. Looking for developers to team up with. I can create models, animations and artwork and I have beginner knowledge of C sharp with Unity. The idea is Inventory menu based gameplay and is inspired by games like DAYZ.
      Here is some early sci-fi concept art to give you an idea of the work level. Hope to work with like minded people and create something special. email me andrewparkesanim@gmail.com.
      Developers who share the same passion please contact me, or if you have a similar project and want me to join your team email me. 
      Many thanks, Andrew.

    • By mike44
      The reference assemblies for framework ".NETFramework,Version=v3.5" were not found. To resolve this, install the SDK or Targeting Pack for this framework version or retarget your application to a version of the framework for which you have the SDK or Targeting Pack installed. Note that assemblies will be resolved from the Global Assembly Cache (GAC) and will be used in place of reference assemblies. Therefore your assembly may not be correctly targeted for the framework you intend.

      Hi
      what to do with the above error in ms code/Unity3d Project on Ubuntu 18.04? I've installed it like:
      https://www.microsoft.com/net/learn/...ux/ubuntu18-04
      Many thanks
    • By Dave Haylett
      Hi.
      I have pulled in five NuGet packages for my Visual Studio 2017 project, however when I build the project, VS spits out 10 .DLL and .XML files in the root of the binary folder, to do with the packages. Can't I shove them into a \packages folder so the user doesn't see these ugly resources next to the .exe file?
      I've Googled moving the packages but the only responses seem to be around moving the installation folder of the NuGet packages on the local machine, as opposed to where VS builds them to.
    • By MoreLion
      hey all! We are looking for members for our Unity horror game! 
      Here’s the story:
      After a deadly virus plunges the world into chaos killing 85% of the human population there are now what they call “zones” these zones are watched very closely by the surviving government, people are checked every day for the virus, even if you touch the spit or any human waste or fluids of the victim who is infected, you will die. But one day, people in the west zone start to go missing, 1 woman goes outside the walls to uncover the mystery, is there more to the virus than meets the eye?, That is where your story starts.
      This game is not a long development game, I have loads other game ideas,
      I will also allow you to have a bit of creative freedom if you wish to add or share a idea!
      And no, it’s not a zombie game lol I feel like zombie games are too generic, in this game you will encounter terrifying beasts!
      There is some concept art one of our concept artists have made
      If interested email liondude12@gmail.com
  • Advertisement
  • Popular Now

  • Forum Statistics

    • Total Topics
      631380
    • Total Posts
      2999673
×

Important Information

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

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!