Dealing with item tied functionality is a nightmare in c3.

0 favourites
  • 14 posts
From the Asset Store
Cartoon Funny Item Interface Sound Pack comes with 426 high-quality sound effects
  • I'm currently working on a video series comparing gamemaker/construct 3/unity across a variety of challenges - from speed of workflow, to engine specific challenges. Currently I'm working on SOLID comparisons. I've worked ALOT with construct in the past, but I've never found a good way to deal with the following types of scenarios. It is the primary reason I hate working with construct and is the one thing holding it back in my mind.

    Here are 2 simple scenario that are very easy to program in a conventional language but are a nightmare in construct events. Performance can also take a huge hit depending on how you implement it. I'm curious to know how others have structured projects to deal with these problems but also retain maintainability and allow for easy additions.

    OKAY, here we go:

    Garages hold buses. A bus can pick up people, people have pockets, those pockets can hold anything but for now we care about wallets and keys. The keys unlock gates, the wallets hold money, coupons, access cards, and credit cards. We need to be able to add and remove people to the bus, items to people's pockets, items to people's wallets, etc. When a bus arrives at a gate it needs to know if any of the people onboard have a key to the gate. At any moment, we need to know the total money on board the bus. The bus has to pay tolls, but each type of bus determines how to take money from people on the bus. People can trade items(including money) while on the bus. Some bills are fake.

    The complexity arises when we begin to add more functionality to items. Perhaps people now carry gems, and the bus is gem powered and each type of gem on board affects the performance of the bus. Blue gyms allow the bus to travel on water, green gyms save gas, etc... Perhaps garages behavior is modified by buses and their contents as well. Perhaps people now have stats and they are happier based on how much money they carry (fake or not- unless they are bankers and then no amount of money satisfies them)

    What if after all that I want some people to carry bombs, and some people to carry guns, and some people to have driving skills...

    There is clean, SOLID ways to code this. But using events... eek.

    Scenario two:

    Characters have stats. They can carry gear. Gear can affect stats while being carried. Gear can be assigned to active slots and add functionality to the character. Not all characters have all stats and only have the stats they need. Gear can add additional stat categories. Attacks can modify, buff, or otherwise change stats. Field of effects can alter stats while in the effect but are removed once leaving the field.

    Stats can be modified by addition, % change, etc... but those also should be ordered (for example a shield could double base defense, not modified defense, while a defense charm could double total defense including all gear etc...). In addition, some effects may only take place if certain gear is present. For example, fluff armor may add the stat "flammability" to a character. A fireball spell may add the effect "burn" to the character. During update, the burn effect compounds based on flammability. Other effects may not change stats, but simply cause the player to smoke, flash, jitter, etc...Some effects may remove other effectors (water removes all effectors caused by fire damage...)

    Effects are unlimited and a character could have 100s of active buffs, mods, etc.

    This type of dynamic buff system combined with inventory is a complex problem, but with constructs current limitations, you will quickly drown in complex workarounds to avoid c3 limitations in functions and custom actions.

    Any useful paradigms for this within the constraints of c3?

  • Scenario 1 - dictionaries everywhere, put dictionaries into containers with objects, then you can definite as many bits of data as required. Or just use instance variables. Then lots of comparisons for whatever is needed.

    Scenario 2 - have a dictionary for each player called "attribute overrides" that always contains an up-to-date list of equipment buffs and such (if changing equipment, refresh this dictionary) so could contain keys like "Jumpstrength = 2" for a 200% increase. Then, have vars in the player such as accelspeed, jumpstrength, hpmax, attack speed. Then have a custom action, or function, that does a sort of "refresh attributes" system, where it starts off by setting the attributes back to the overall "default" value (E.g. Jumpstrength gets set to 300), then throughout the custom action, multiply the attribute by whatever keys it can see, depending on the key name (so the players variable "Jumpstrength" which is currently at 300,gets multiplied by the Jumpstrength key in the players own "attribute override", meaning it gets increased 200% and ends up at 600).

    EDIT: Your video where it would judge speed of work flow and such, I don't know much about making videos or comparisons, but if say you asked me to demonstrate these 2 solutions, then for each I'd whip it up in less than 10 mins from scratch, adding more time for each extra attribute or gem type and such, using built in behaviours and plugins (and assuming I use rectangles for artwork since I'm bad at art).

    Since these are new solutions for you to think about, you might take longer with speed of workflow unless you have done these systems previously (I'm not assuming you know less or judging your competence with C3). But, if you told me to whip up an RTS with moving units and pathfinding, then I'd definitely be much more slower and unsure, whereas others would be lighting fast at this. I don't quite know what my point is but, I see event sheets as powerful and doesn't work against me, and you have titled this post as C3 being a nightmare and such, which is just baffling to me as someone very familiar with event sheets and don't often have issues with it and feels like I can get any result needed.

  • Simple example and implementation:

    A field object.

    A family of characters.

    A family of functionality card.

    When a character enters the field, the field stores a reference to the character and hands a function card to the character. When the character leaves the field, the field removes the function card from the character. Fields can overlap and characters can hold multiple functions. The function card dictates what happens to the holder of the card.

    Simply tracking "On collision exit" becomes a bit of a pain, much less adding refferences to arrays and dictionaries that should be in a container, but can't because families. So instead is back to picking by uid and alot of for each conditions which kill performance at scale.

  • That's a simple example but anything becomes bit more trickier when scale is involved. Would there be 10s,100s, or 1000s of characters? Are these AI bots or online players and 1 local player? If just 10 players or so and not much other intense collision stuff going on elsewhere in the game, then tbh it's really not gonna impact too much with basic collision system in events, but assuming scaling up means 100s of characters, then splitting up collisions and picking and such would be beneficial, although this could impact a local player so could always check for their collisions very often, but if its online players or bots, then shouldn't really be that impactful.

    I find I use "for each" and pick by UID very often with hundreds of instances, and performance is great overall, I guess it's mainly about trying to minimise what the "for each" is cycling through, and trying to, uh, "functionise" everything, so the game is more reactive rather than always checking things every tick (which, in the case of checking for multiple field overlaps, becomes tricky). I'd be curious to see performance on a basic collision system for this field example, I have feeling it'd be pretty stable unless talking thousands of characters. Also there's collision cells which I think are on by default, so if fields overlap but are spread across the map, then collisions get managed pretty well.

    In comparison to gamemaker/unity, how hard is this example to do, like what would be the quick rundown of what you'd do in those applications?

    EDIT: with the bit about families and containers - 2 points I'd bring up: you can have a var for the family that stores UID of the array. Yeah, more picking, but as long as not picking every tick and thousands of instance, then this should work well. And, perhaps worth considering having the characters as a family of skins for a regular square sprite called Player or something, where the player object handles the movement and such, and the chars are children to the player.

  • I'm currently working on a video series comparing gamemaker/construct 3/unity across a variety of challenges - from speed of workflow, to engine specific challenges.

    ....

    If you are strong in programming, you can use JavaScript and all its power.

    Maybe you just don't know how to use this tool.

    Do you have successful games or do you just like to compare and test?

  • I kind of agree that the scenarios described are kind of a nightmare to do with events... which is why I use javascript for that stuff. I'm not sure how that could even be reasonably implemented via eventsheets without being cumbersome.

    And I don't think it needs to be implemented via eventsheet. If you're a beginner, you're probably not gonna go ahead and attempt to create complex systems like that. If you are not a beginner, time to step it up and jump into javascript.

  • When picking becomes a problem I try to do things in a way that does less picking that looks cleaner if possible. One idea is to make getter/setter functions with uids. Another is to use one data structure object such as json to store all the data.

    JavaScript works too but it has its own set of annoyances so pick your poison I guess.

    I’m always trying to find ways to do stuff in simpler, cleaner ways, and some kind of system on top of events to abstract away complexities often come up.

  • With the examples in the original post, I think it would make more sense to provide your solution in other engines/languages and the problem you forsee implementing in Construct since your claim is that Construct causes difficulties.

    I do agree that sometimes I find myself wishing I could quickly use a .map() to achieve something - and yes, you can use JavaScript in Construct. As others have said, it is preference.

    But with events, I don't see why you couldn't achieve either of those with mostly instance variables and functions.

    Picking in events is essentially filtering, which can be done on instance variables. Functions can have return values - if you want to break out complex logic into re-usable bits. tokenAt and tokenCount can be used if you want an instance variable as an array. If you need an array of objects, you can use tokenAt and uids. If you want an array of custom objects, as opposed to instances, I prob would recommend JavaScript.

    My point is that these are the same tools you would use to achieve your functionality in any programming language. It does seem a bit awkward at times, but that is because Construct was originally built as a no-code engine. So there are some trade-offs there - think target audience. However, I'm a programmer and still enjoy using Construct. I'm not actually sure why, but I do!

    but with constructs current limitations . . . to avoid c3 limitations in functions and custom actions.

    What are the limitations? Can you elaborate?

    Also, I'm not sure who said it, but I don't think it is fair to say a lot of loops or for eachs would kill performance. Of course a certain amount of loops will kill performance - anywhere, any engine, platform - but I've seen people act like adding a couple of "for loops" into their project is the end of the world. I'm just saying, test it to see if it actually does, especially if you are making a video comparing engines, ha.

  • TLDR: you said use scripting. I agree, but then the benifits of the event system and constructʻs core strengths (speed and ease) start to diminish.

    With the examples in the original post, I think it would make more sense to provide your solution in other engines/languages and the problem you forsee implementing in Construct since your claim is that Construct causes difficulties.

    In a component based engine where classes are components, example of one class:

    //Add to any object that needs inventory

    //Overide to add specific functionality

    Class HasInventory {

    List<Items> inventory = new List<Items>();

    public void AddItem (Item item) {

    Items.Add(item);

    item.OnPickup(this)

    }

    public void DroppItem (Item item) {

    //check if item can be dropped and is in inventory

    ...

    item.Ondrop( this );

    Items.Remove(item);

    }

    }

    Class ExampleItemHealthSucker : Item {

    private HasHealth heldBy

    Public void OnPickup(HasInventory temp) {

    heldBy = temp.getcomponent<hasHealth>();

    Public Update() {

    //do a thing to the holder

    if (heldBy!=null)

    heldby.modifyHealth(-1);

    Public void OnDrop(HasInventory temp) { destroy(this.gameobject) }

    }

    Take the above code, slap an inventory component on any object that needs it, and add item to any object that can be collected. You need alot more to make this water tight and dummy proof, but that isnʻt the point.

    Any additional behavior and you can inherit from either class. Maybe a thief picking up a wallet always removes the money from it and adds it to his own pocket. Maybe the vampire medalian only remove health from its owner if a silver cross is also in the inventory. Maybe the bomb explodes and kills the owner and all inventory both up and down the chain. Maybe many things, but all things can be handled by a script attached to the object handling it.

    I do agree that sometimes I find myself wishing I could quickly use a .map() to achieve something - and yes, you can use JavaScript in Construct. As others have said, it is preference.

    I agree, this is something javascript should be used for. But... then the strength of construct, no longer applies. If I am authoring plugins/behaviors and scripting... VS/C# and unity wins almost everytime. Simply on grounds of performance, scale, ability to keep projects SOLID, etc...

    Construct has some major +++s but right now, the scripting environment isnʻt as good as other tools.

    But with events, I don't see why you couldn't achieve either of those with mostly instance variables and functions.

    Because the number of effects, items, types, etc... isnʻt known. if you know a character will only have two stats, you can hard code it. Easy, happy to do that in construct- If you know there is only water and fire effects, you can hard code it. But creating a system to handle nTypes, nEffects, etc... Not so easy. You need arrays or dictionaires for tabbing UIDs of objects, you have to iterate through those objects. Callbacks in the form of strings used to invoke functions are needed - and probably some scripting so you can avoid the silly function maps.

    I prob would recommend JavaScript.

    Me too, but then, what advantage does c3 then have over unity? or monogame for that matter?

    My point is that these are the same tools you would use to achieve your functionality in any programming language. It does seem a bit awkward at times, but that is because Construct was originally built as a no-code engine. So there are some trade-offs there - think target audience. However, I'm a programmer and still enjoy using Construct. I'm not actually sure why, but I do!

    Agreed. All points. I keep coming back to construct over and over again.

    > but with constructs current limitations . . . to avoid c3 limitations in functions and custom actions.

    What are the limitations? Can you elaborate?

    Encapsulation, private properties, custom actions canʻt return a value, functions are always in the global space, all objects are in the global space, you canʻt attach functionality to objects through events (you can make behaviors though).... Like you said, construct is swift in the right circumstances... but at scale, it can be unweildy if you donʻt build your own pluggins and behaviors

    Also, I'm not sure who said it, but I don't think it is fair to say a lot of loops or for eachs would kill performance. Of course a certain amount of loops will kill performance - anywhere, any engine, platform - but I've seen people act like adding a couple of "for loops" into their project is the end of the world. I'm just saying, test it to see if it actually does, especially if you are making a video comparing engines, ha.

    Foreach and function call events have performance overheads that are significant depending on the gameʻs scope. The more you do in a funciton, the less the overhead matters, but if you have routing functions only to do a single action, you are adding alot of overhead to that single action. Obviously, this will matter only in some types of games.

  • Jase00

    Scenario 1 - dictionaries everywhere, put dictionaries into containers with objects, then you can definite as many bits of data as required. Or just use instance variables. Then lots of comparisons for whatever is needed.

    Yes, but instance variables are out. You canʻt dynaically add more at run time - so it is arrays and dictionaires all the way. You canʻt use containers because they donʻt work with families. So you need dictionaires, picking, functions, custom actions, etc... And all this is much more cumbersome than a simple script. Once you are scripting in c3, you are acknowledging the limitations of events and you could be coding in any environment where coding is better supported.

    For the the pure implementation comparison videos, I typically get the logic figured out ahead of time, practice the solution and then do several time runs. Right now, Iʻm investigating the best way to build this using construct events (no scripting atm). I use as many hotkeys are available to me, and memorize any script calls I need, and make and use a cheat sheet. Itʻs just measuring the flow of the tool and the ease of adding on.

  • igortyhon

    If you are strong in programming, you can use JavaScript and all its power.

    You can... but that isnʻt the point here. "without coding" is kinda both constructs claim to fame and one of its pros. The moment you have to start authoring behaviors or plugins, you may as well be in unreal/unity/monogme/gamemaker - but that is a different video :D. c3

    Maybe you just don't know how to use this tool.

    Lol Maybe... do you?

    Typical. The one person on this thread with the fewest years experience is the only one to share this profound nugget of wisdom. Everything makes so much sense now, canʻt believe I never thought of that. Please, elaborate, so much insight.

    Do you have successful games or do you just like to compare and test?

    Even more irrelevant... Maybe. Do you?

  • Jase00

    ... if fields overlap but are spread across the map, then collisions get managed pretty well.

    In comparison to gamemaker/unity, how hard is this example to do, like what would be the quick rundown of what you'd do in those applications?

    In Unity... It would be very similar to construct at the abstract level. The difference is that functionality can be expressly written into a class while you would do it with events and picking in construct. The harder part with construct is creating a robust system that can easily allow for additions and subtractions of items at the design level. In unity You would add a collider component to the field and characters for hadling entering and exiting feilds. You would also create a class for effects, and a class for the affected. You then write a class for fields where they store a reference to an effect. Any effect would have base behaviors and derived effects would describe specific functionality. When a character enters a field (using inbuilt collision system), it would store a copy of the reference to the effect. When the effect updates, it would callback to the character to do a thing (like subtract health, etc...).

    A basic system like this is pretty fast to program (you COULD even do it in javascript for c3) but Iʻm trying to do this with events.

    The bigger dif is that unity uses component architecture and classes can inherit from classes to n depth... Construct only allows one level of depth to inheritance with its families. While you can add an object to as many families as you want (mimicking components) you donʻt have a good way to extend them to provide further functionality.

    Iʻll have a capx to share maybe in a bit. I donʻt think the collisions are an issue at all unless you are dealing with RTS level scope and every unit attack creates a field. Even then, construct does a pretty good job with collisions. The bigger issue is that construct

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • Thanks for writing out the logic of how you would achieve that elsewhere. My brain is a bit foggy today and I'm not in a good spot to write out all of the logic, but let me tell you how I would get started.

    This is a non-scripting approach, as I agree, the scripting side of Construct removes a lot of the advantages of Construct.

    Create a Family. I'll call it inventoryFamily for now.

    Think of the Family as the equivalent of a class. Of course a lot of differences though. Adding sprites to this family will add all the behaviors, effects, and instance variables to the sprite.

    To start, will add a string instance variable to the inventoryFamily. I'd just call it inventory.

    Create a function for addToInventory. One param will be uid, which first condition will be to select inventoryFamily by UID.

    Now you are sure you have the instance you want. Another param can be the inventory item to add, whether it is a word or a reference number. Adding would set inventory = inventory & "," & param (if inventory has value). You can also use scripting in an event, if you want to make use of like .split(',') .push and .join(',').

    If another action needs to be taken I'd make another function for that. Sometimes I'll make a function for each different action and other times I'll make one function and pass a param for the different actions. Of course the latter turns into a long event of elses, but if it is only a few actions for each sometimes I'd prefer that to keep them organized and not pollute the function namespace.

    This also isn't my favorite, but I'd probably do call function by name. That way, the name of the follow-up action (removeMoney, modifyHealth) could be stored wherever you need it (with the item type; if action from the carrier, would create an instanceVariable on the inventoryFamily of pickupItemAction; or pass the param into the function).

    It would be great to be able to attach a function to an object such as a method, but the functions that first grab the instance by UID achieve the same thing.

    custom actions canʻt return a value

    Does creating a function with a return value not take care of this?

    As for some of the other limitations you mentioned - let me remind you my brain is foggy and also mention you prob know more than I do, so this isn't disputing what you say - but I'm not sure about some of the direct downsides or limitations. For example, in general you don't want to pollute the global space, but what advantage is it if all Construct objects were NOT in the global space?

    I kind of suggested using scripting because it sounded like you wanted to adhere to common programming practices. I almost always use events, unless I want to hit an API or its code I want to use elsewhere.

    I think the thing I like the most about using Construct (non-scripting) is the syntax check. It will prevent you on the spot from using a variable that isn't declared or missing a comma or period somewhere. It's not really even a pain point in my normal dev/day job, but its just nice that when you build your project to preview your game, you don't have everything breaking because of a typo.

    I know that didn't address everything, but maybe it's all I got, ha!

  • I'll have to dig into the way you suggested doing. So far my approach looks totally different and I am interested to compare performance. I think off the top of my head, there are some issues with the strings as it can more easily lead to soft bugs.

    Does creating a function with a return value not take care of this?

    It does but then you have to make your own system of overriding functions. Custom actions can be overridden by family members, which is real nice and starts to tap into the sweet sweet polymorphism.

    For example, in general you don't want to pollute the global space, but what advantage is it if all Construct objects were NOT in the global space?

    If I was programming in monogame/c#, I would explicitly dictate what scope var, classes, structs, etc... are in. Then, I have the advantage of controlling access to only the places where those objects are needed, useful, etc... It also makes it super easier to work with collaborators, as they don't need to about the 100 private instance variables you use to make collisions work. They only care about the publicly exposed methods... such as "OnCollision".

    I think the thing I like the most about using Construct (non-scripting) is the syntax check. It will prevent you on the spot from using a variable that isn't declared or missing a comma or period somewhere. It's not really even a pain point in my normal dev/day job, but its just nice that when you build your project to preview your game, you don't have everything breaking because of a typo.

    This is why when I have to start scripting in construct, I usually move over to a game engine that has a better environment for it. Strict and autocomplete is a must and a lifesaver for me. But I'm biased. I started with pascal and moved to c++. Python and javascript drive me bonkers. I get the appeal where it has advantages, but honestly... I really, really like c#.

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)