Ruskul's Recent Forum Activity

  • piranha305 - I definitely thought back when c3 was first being discussed that there was a plan to be able to define behaviors in the editor, and even use events to do so. I'm not sure where the disconnect was, but my entire perception was that c3 was js based so could be used anywhere, and more modular with better customization. Also, allowing compartmentalization would be nice too (I should be able to define a function available to only a particular event sheet, for example)

    Imo, that would be absolutely huge to be able to define abstract functionality that you can easily share between other projects and users. Bolt for unity was built by one guy, (it is a visual scripting tool) and it exports semantic c# scripts.

    Thanks for c3ide, btw. I dig the gui and adding aces to code where I simply plug the info once and it takes care of all the tedium.

  • Hey Overboy

    I was curious if you have a timeline for migrating to sdk2 on these tools and if you know offhand if any will have trouble due to internal api use?

    I use modified versions of mostly array and dictionary as behaviors but I am trying to ensure I don't face troubles in the next year or so on this current project.

    Mahalo!

  • This is in response to the last page located at : construct.net/en/forum/construct-3/plugin-sdk-10/addon-sdk-v2-182122/page-12

    On the topic of coding limitations and design patterns in c3.

    I'm looping in Jase00 and skymen as I had remarks for both their responses.

    First for skymen , I'm a little confused, because in regards to family nesting, inheritance, etc... I thought I was making the points about construct 3 that you made. So we might be on the same page? I didn't touch data flow, but I was meaning to use patterns as an example as to show some of the deficits that construct has in regards to addressing certain types of games. Those deficits would sometimes cause me to try to solve the probelm via the sdk, where I would be met with shortcomings within the api. For me personally, many of those short commings have been alleviated with the addition of a few news calls (like being able to use collision cells, etc).

    For Jase00 - I'll write a second post concerning some of your solutions. I agree with some, though I think others are not feasible/ need clarification, but as a summary, I'll point out a few things.

    1st, I should clarify that "not possible" and not "reasonably possible" are two different things and I think I am arguing the second. Values and expectations will come into play when we try to define reasonable. I think you are right to point out that in particular instances, I am arguing a problem is impossible to solve when it is in fact possible, so I think I need to clarify them.

  • Ah gotcha, I was doing alot of js blocks to test out script interfaces, so I wasn't in vs atm. I suppose I could write it there and then just paste it to blocks so I don't make dumb mistakes like this.

    I am of the belief that, overall, you can make whatever 2d game you need with whatever system the game needs, whether stacking buffs, equips, anything. Maybe not one-click out of the box, but, idunno, I don't touch JS and yet to hit any dead ends with some wacky ambitious ideas. I'd be curious to know what isn't possible in events or sdk-safe JS that can't be done for making any 2D system. Or is it to eek out more performance by using these undocumented js functions?

    Jase00 - ugh, I wrote a novel. TLDR at the bottom, but I'm not sure it makes sense with the context. Nobody should feel the need to read this, but I'll post it anyway. The reason I tended towards using internal api is to increase performance where I could, or avoid repetition. More on that later

    I think you are already familiar with some of my complaints, and I'll clarify as best I can. If you have a game with a fairly rigid organization, known ahead of runtime and before devtime, then producing it in c3 is fairly straight forward. Producing a game like Mario is a breeze. Making a game like pokemon, only slightly more difficult.

    The stacking buffs example is actually a good one. If you know in advance the types of buffs that can exist, and the number of stats they affect, then making a system for that is easy. When things become difficult is when you don't know how many types of effects will exist before hand(or the number is high), nor the objects that can be affected. Some a problem is best solved by a completely abstract system to accommodate designers future ambitions.

    Take mario as an example: We have a character that can walk/run, duck slide, wall jump, and a few other things. Now lets give mario an inventory for items that can alter his stats, and even add completely new abilities. Without deep oop, or ecs architecture, the easiest way to solve this problem is to embed conditional branches in mario's behavior logic that checks for items containing abilities. Not too bad... so far... especially if the number is low.

    But now extend that to all characters and allow any character to have any ability. Now every character needs conditional branches. Now keep adding abilities into the game. With new abilities might come the need for new stats, etc... and the base class for characters is getting hefty.

    The issue at this point isn't that you can't make the game, Its that you've created a situation where adding a new behavior to a single character requires a deep understanding of all characters and all abilities in the game so far, typically requires modyifying the code base in multiple places, and if you want to remove an ability, you have to remember all the places it has code for it. This becomes unmaintainable. A single developer likely can do this if well organized- but only to a point; and as part of a team - this is nearly impossible if you have two people working on the same system.

    The second issue is that every character, from a performance standpoint, no matter how simple, is as complicated as the most complicated character, as every character needs to have a conditional tree for every ability it could posses and the stats to go with it. Every object is bloated with data it doesn't need and this can hurt performance. We haven't even gotten to input support, ai support, or flexible and dynamic stats, etc... which also has to balloon to fit the conditionals. If you take a game like vampire survivors, and require bullets to also have abilities or dynamic effectors, you now have a code structure that kills performance so completely you have to do something about it.

    So the need to change can be 1 or both. Poor performance, or difficultly adding, maintaining, or removing code.

    Enter ECS or OOP. The problem outlined above is a fairly classic problem and there are multiple ways to deal with. My favorite is ECS, but i'll cover both and why they aren't great in c3.

    In either system, the goal is to create a framework that efficiently facilitates adding functionality to objects arbitrarily at runtime, and reduces the complexity of maintaining and creating new functionality in this environment. Good for the game, good for the developer.

    But construct makes this incredibly difficult, as it can't natively do OOP or ECS.

    As will be pointed out, you can code javascript. Construct has a great event editor and that is a major selling point. It is fast to iterate with and I like it. That is why I use it. But coding in c3 is more verbose and difficult to learn than pretty much any other engine I have used. I started in c2, so I should be biased to it, but I am not. Creating addons is painfully slow compared to creating editor integrated scripts in unity, and simply coding bare functionality in c3 takes more writing to achieve the same results in unity. That isn't to say some people may still prefer c3 in this regard, but it is a factual claim to say it takes more typing to get the same thing done in c3 than in a program like unity. Other game engines are much better organized at a architectural api level, so the power and flexibility is also typically higher than in construct. Compare the unity collision api to constructs and you'll get a simple overview of just how tiny construct is in comparison.

    OOP in c3:

    Families currently allow only one layer of abstraction in construct, (since you can't nest families in families), so to create a better OOP system in events, you have to get creative. You can simulate deeper structures by adding objects into multiple families, and then use a uid picking framework to select multiple families from a single object. The organization of these families and which objects go in them has to be documented and remembered by the dev. You then can assume (as the dev) that any objectA in some family will also be in another family that it "inherits from". If you want that base functionality, you then pick that family by the object uid you currently have picked. But this forces every object to now require complex picking boiler plate and for each loops (which reduces performance). Worse, you also have to repeat the boiler code events for every level of depth you wish to add for an object, and for every structure you create, meaning the exercise of abstraction isn't itself abstracted - this also creates significant event sheet performance overhead. If you also want inherited custom actions, you now have to repeat the creation of those actions for each family in a tree, and link them together. Every object added will require this boiler plate. The deeper the structure being simulated, the more events per object that have to be run and the more boiler plate you need to set it up, increasing dev time per object added. So even if things were running okay, once you add this system to ease development, your performance may likely go down. It will depend on how many abilities you have in game, vs the average number per object, contrasted to the depth required to simulate OOP. The processing power per empty event is not trivial when duplicating structures like this with nested loops. I have found this method to be cumbersome to set up and don't recommend it. It creates less complexity to add and modify abilities or effects, but is itself difficult to maintain at any scale and tedious to expand.

    If construct allowed families to inherit from families, this would be a non-issue. Another solution would be containers, but containers don't work with families. So you have a weird dynamic where you have to pick your poison.

    ECS

    :

    Setting up ECS is easier imo, but similar to simulated OOP, it requires repeating boiler plate for every system you need to handle ECS. In a nutshell, you have to use some OOP style systems like described above, but you use it only for attaching the ecs behavior to objects. The depth of the oop is shallow, but it still requires boiler plate per added object type. Basically you create a family for "systems", and family for "components". The raw requirements for these families is that the system handles component addition and removal, and routes functionality to the components. You can connect this via UIDs or create a parent/child graph and use that. In a platformer example, a system could be a character handler, and a component would be the ability or behavior. The handler performs the abstract task of routing input and state to the active behavior. Some people use state machine type logic where the components define transitions to other behaviors, but I prefer the transitions to be handled without a component specifying the components it transitions to.

    At the end of the day, for ECS, you will have to use a Foreach loop for every system, and then loop through the components, running various custom actions like "Check" "Enter" "exit" "update" etc... The overhead of the small scale OOP to pick related families, dictionary lists of components, etc... and then dynamic routing of state/input through components, means that the performance overhead of each system scales with its complexity and its possible to achieve better performance than the simple/basic branching conditionals if the project is complicated. Also, adding functionality is as easy as the system is designed for, and allows the functionality to be self contained. apart from the fact that you still have to boiler plate every object.

    In other words, if I want mario to be able to do a ground pound one day, I clone the object type "BehaviorComponentsTemplate", name it "Behavior_groundPound", copy the template eventsheet for the boiler plate (replacing appropriate code), and then manually add every action needing overridden, and then add in specific functionality. The actual functionality is faster to write than setting up the boiler plate. Then add an include in the correct event sheet and go.

    The payoff? All the functionality for ground pound is now a component I could add to any character, without having to add conditionals, and all contained in one place instead of being in multiple locations.

    The downside: This system requires a lot of nested loops and picking so you do take a performance hit over a manually crafted rigid system. Any character that has rigid abilities should still be manually coded, meaning you now potentially have duplicated code all over or need a simple light weight router system. Again, this is alot of work to set up, and requires ongoing boiler plate management; Meaning if you change the system, you may have to change every single bit of boiler plate through the entire project.

    BOOM. That is BAD, BAD, BAD.

    TLDR

    So basically, c3 forces bad types of programing in order to avoid bad types of programing while solving complex/advanced problems, and the solutions themselves are difficult to maintain or scale. You avoid one issue, but create a different issue.

    The takeaway? Construct isn't the right solution for a sufficiently complex project with high dynamic object counts. It literally becomes a case of, this isn't possible, and better off in a different engine.

    For my own project, I really wanted to finish a game with construct, so I went from a bullet hell style game with high numbers to a game with fewer but much more impactful threats. It actually turned out to be good for game feel, as it reduces noise and makes choices more meaningful. Each enemy is an intimate threat, instead of one of a 1000.

  • Okay, Cool, cool.

    ... Uh, Does type script work within the c3 environment?

    I thought typescript only works outside of c3?

  • Cool, Thanks.

    And I would add that while I "can" do many things correctly when it comes to my hacks, but constructscorrect way is via very terrible coding practices, duplicating code, and other bad practices.

    The advice to not do something is actually advice to move to a different engine; An admittance that c3 is not the right tool for the job.

    Had a private api existed in 2014, I personally never would have bought construct 2. I never would have bothered with construct at all for that matter, as several rex plugins gave me the ability to jump in and start doing stuff that c2 wouldn't let you, and recently, I certainly wouldn't have made the decision to port my existing project into c3. I'll also point out that being able to download and tweak existing vanilla behaviors was also the only reason I started using the sdk in the first place. Without that, I would have surely skipped. I think many others would agree; The addon devs made construct viable for alot more projects than you can imagine, and the result was more paying users.

    Construct is great in a few areas, and very lacking in many others. Addon devs picked up that slack, and this isn't to underrate the work you have done Ashley .

    I don't have the data to make such claims credibly, but I don't think nearly as many people would have used/adopted or stuck with construct if add-ons had been limited the way you wish they were back in 2014.

    This whole issue makes staying with construct less advantageous than I perceived it to be 6 months ago. This CAN change with a solid API, but looking at the rate of development in the last 10 years, I'm not holding my breath, and I think that's fair.

    Ah... well... that makes sense then, Ashley

    And on point #2, I agree as well mostly, which is fundamentally why I took your advice last spring a cleaned everything up. I personally used internal calls because it was convenient - and more performant in some cases, but I also stopped prematurely optimizing everything and try to not worry about it.

    Here in a few months, I might actually be to a point where I can fundamentally prove whether or not the performance concerns I had are really concerns at all.

    As an aside, and just to further plug my agenda, Nested families or shared behaviors across families would solve so many problems (any way to prevent the cumbersome alternative of nested foreach loops and picking companion objects by uid.) Extending behaviors would also be a huge gain for scalable design and good coding practice. Custom actions are a start on this, but being unable to nest families or share behaviors creates a situation where you still can only go one layer deep into abstraction, which basically precludes any deeper/complex custom systems being constructed via events.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • Wholly barbecued barnacles. You have no idea how long I stared at that even after you pointed it out.

    Thanks.

    As to the second point, is there a way to prevent users from accessing functions we want to keep private?

    I have typically structured my addons the way it is recommended, with properties and methods in the main class and all ACES simply calling getters/setters/and functions, but I always had some functions that shouldnʻt be accessed out of order or context and I donʻt really need them exposed to the scripting side in c3 editor.

    ex:

    //Some complicated task
    DoSomething() {
    	_doPrepThingA();
    	if(_checkThingB()) {
    		_doSomething();
    		_resolveThingA();
    	}	
    }
    

    Where DoSomething wraps up all the things that need to happen everytime a user would want to do the thing. But, _doSomething does that thing without the additional checks and is used internally for performance reasons, but shouldnt be available to the user.

  • Okay, do you know if there was any plan to allow such a thing? Or is that problematic?

    Currently I have been moving more and more functionality into scripts or addons as that allows me to avoid event sheet forloops, but I still preffer the visual nature of event sheets over scripting.

  • Hello,

    I am having trouble accessing behaviors from js in construct:

    For example. I am using the sdk2 sample behavior.

    const o = runtime.objects.Sprite.getFirstInstance();
    const b = o.behaviors.MyCustomBehavior;
    o._setMyProperty(5);

    I get the error that _setMyProperty is not a function. In the v1 of the sample behavior, the script interface maps to _setMyProperty.

    The manual says to simply put what we want to access via script in main class.

    Which, I am trying to understand. Wouldn't this mean we have no ability to prevent users from accessing a behaviors runtime methods or properties, or will #private protect them. I wanted to test this, but atm, I can't access anything from script on v2.

Ruskul's avatar

Ruskul

Member since 23 Nov, 2013

Twitter
Ruskul has 2 followers

Trophy Case

  • 11-Year Club
  • Forum Contributor Made 100 posts in the forums
  • Forum Patron Made 500 posts in the forums
  • x6
    Coach One of your tutorials has over 1,000 readers
  • Educator One of your tutorials has over 10,000 readers
  • Regular Visitor Visited Construct.net 7 days in a row
  • RTFM Read the fabulous manual
  • Email Verified

Progress

18/44
How to earn trophies