BeatsByZann's Forum Posts

  • Monday, March 3, 2025

    8:20 PM

    I've only been using Construct and developing for six months but I've come up with a few tricks to keep my own sanity that I haven't seen in this thread yet.

    1. Use "Lookup Variables" for strings that need to be passed as parameters in expressions/conditions.

    a. e.g. Prevents having to remember the tag string you used in a timer/timeline/flowchart somewhere else and from mistakes from typo's.

    2. Use families to create organized "variable groups" within objects you are using as global variable banks.

    a. You can reduce the number of global objects you use as global instance variable banks.

    b. Looks cleaner and more organized when looking at the instance variables in the Properties bar

    c. Keeps the global variable name space tidy. Just enter the family name of the subset of variables you want to enter into an expression and get a smaller autocomplete set.

    d. My examples below show me using this for a set of lookups (I think of these as the construct version of lookup tables) but you could use them for any set of variables in a global variables object.

    3. Make logging work for you and your visual style.

    a. The more complicated your project with logic broken out in more event sheets, the harder it is to debug.

    b. Create your own logging functions to give you information in the console that makes sense to you and helps you debug your particular logic structure

    Here is a vertical slice example of how I use "lookup variables" in family-organized "variable groups" to support my logging functions which give me visual clarity into what my application is actually doing.

    Set up multiple families for each group of variables on the same object:

    NOTE: Here I am using ForConstruct's "Globals" object plugin, but you could do the same with any sprite on a layout that you mark "Global"

    Now create FAMILY-LEVEL instance variables on your global object:

    Now your variables (in this case they are being used as global constants) are divided into easy to read groups (Construct 3's version of lookup tables in this case).

    NOTE: I am creating an underwater game, so I was actually able to find corresponding emojis to many of my enemy types. I use them in the console logging because I'm a nut.

    Now you can search the autocomplete namespace by using the family name when using in expressions:

    Now for my logging. My undersea enemies have complex movement that can't be replicated by a single movement behavior in Construct 3. In order to get the different movement patterns I want I have a hierarchy of actions named Phases, Modes, and Behaviors.

    • Phases call a specific Custom Action which starts a specific movement/animation combination. (e.g., signal you are about to move, move to location, pause for a duration)

    • Modes are combinations of one or more Phases

    ○ e.g. Mode "Pulse" = Signal Move, Glide to position, Pause

    ○ Mode "Drift" = Drift slowly to ocean floor.

    • Behaviors are combinations of or repetitions of Modes,

    ○ e.g. Behavior "Rise_And_Fall" = Set Mode "Pulse" repeated till you hit the surface, then switch to "Drift" until you reach the ocean floor, repeat.

    But I have this logic spread out among different event sheets, Phases and Modes in their own sheet and Behaviors in a master Enemy Logic sheet, plus another sheet for Advanced Signal Listeners (Thanks Overboy!) and triggers, which control the shifts between the different states

    . It can be tough tracking bugs across all this.In this console screen shot I use indents and icons to visually show the filtered behavior of a single enemy and I can see its entire behavior cycle across my various event sheets.:

    I can now see the enemies AI lifecyle in a visual manner that makes sense to me and allows me to easily spot when things are happening out of order or are missing. For instance:

    All game events call signals to logging functions so I know exactly what is happening, for instance when a player is struck by an enemy:

    The attack is resolved in its entirety before the system moves on to anything else.

    But when I hit an enemy with a projectile I see this:

    The game is allowing the enemy to call parts of its action AI before the attack is entirely resolved. Even though this isn't causing any bugs right now, I know this is asking for trouble. There are almost certainly going to be bugs where an attack results in an enemy death, but the proper animations don't fire because its calling the AI which is also messing with animations and variables. I now know I need to fix this to avoid problems in the future.

    Anyways, curious to see people's thoughts on the above. Has anybody been using families with global variables like this already? Does anybody else have console logs that look children's picture books? Am I just weird with this stuff?

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • MoveTo behavior: construct.net/en/make-games/manuals/construct-3/behavior-reference/move

    Just use the action Move To Object (and select the player object). You can set parameters right in the layout properties bar

    Does not move around solids, only stops on them.

    Pathfinding behavior: construct.net/en/make-games/manuals/construct-3/behavior-reference/pathfinding

    Can be used by itself or in tandem with MoveTo. In that case you are using the pathfinding to find the path around obstacles to the player, but you are using the more natural movement style of MoveTo (compared to pathfinding's constant speed).

    Example projects to learn/copy from are at the top of each entry.

  • kindeyegames.itch.io/c3-3dobject-alpha

    kindeyegames.itch.io/construct-3-rotate-3d-behavior

    I'm just starting to learn how to use these add-ons myself. Remember to read the instructions and set your z axis scale to "regular", that was messing me up at first.

    The downloads include plenty of examples to learn from.

    I'm actually working on a very similar scenario: 2D shooter with some levels infinitely rotating around a point. Mine takes place underwater though, I wanted to have an underwater volcano or shipwreck in the background, etc.

    One challenge though... most 3D construct examples I've seen so far involve laying the camera parallel to the canvas. As far as I can tell that's not going to work for a 2D shooter, so I've been really playing around with z-elevation and merging 3d elements into and out of the play layer.

  • I don't think you actually need mapped functions in this case and others above have suggested valid alternatives. However, you said you couldn't get mapped functions to work so I made an example in case you ever need to use them in the future, or your hat system gets so complex that you DO need them ( I could maybe see this being the case if the differences between hats becomes something beyond just additions to attributes).

    In any case, my example also shows how I would do it, by making the hats objects each with their own attribute alteration variables (you could add booleans too). I'm assuming that even if the different hats are included in your player animations that they will still appear on the ground or in menu's, HUD, etc so that they will still be objects within the scene.

    I created a family for hats and created family instance variables for all possible attribute adjustments. That way my functions can be completely generic to the specific hat type and just add all attribute values or subtract them. Since its adding and subtracting from the existing variable values you don't have to worry about any fHat vars with "0". So from my example you could just remove the function maps and call "add_Hat" and "subtract_Hat" directly. You are picking the hat to add by comparing a chosen string to the object type name of all fHats in the layout, and the subtract function picks similarly based on the vCurrentHat value.

    If you didn't want a ton of separate object types for whatever reason, you could have one hat object and create all your hat types as templates in a repo and then just load them into the layout off screen somewhere. In that case you would want to identify each object instance with a hat type identifier variable, e.g. "vHatType" = "Blue" in the templates. Or, as I said above, use some of the other suggestions in the thread.

    I'll be curious which way you choose to go if you want to post a final update in the future.

  • Overboy As with many others I absolutely love all your addons, especially your Advanced Signals and your JSON and Array behaviors. As a new Construct user (5 months) your tools made intuitive and filled in functionality gaps I was already seeing even after a month of Construct. I don't think my early Construct experience would have been as fun or productive without your tools. I'm really sorry that things ended this way, your work is and will continue to be very much appreciated. I already downloaded fedca's update and breathed a sigh of release. Do you think you could post here when you hear about a new SDK2 port of your addons so that all of us who use them can update ASAP?

  • Wow! Talk about a pleasant surprise! Somehow I missed this notification of your reply until today, and you went ahead and made a plugin. Thank you, I can't wait to try this out! As you said, I've been making more and more use of the console as my game gets more complex (and more reliant on triggers) and just stepping through the sheets with the debugger gets unwieldy.

    You want to know what I was doing in the meantime? I was trying to teach chat GPT to take my event sheet files and add a string variable under each event group header with the event group title in the value. I then had a logging function I wrote that always took that variable as the event group location, with each nested level getting an incremented integer added to the end. I was uploading my entire zipped project file, having it make edits at its end, and then providing me with a download file.

    The results were... frustrating. I actually got it to work once, on a couple event sheets, but it kept on screwing up something in the chain. It would make the additions but miss the nested groups. It would get all the nested children, but then mess up on generating unique SID's. It would get everything and it would look good in the preview but then it couldn't generate a download link for some reason. I spent many hours trying to make the thing do everything correctly consistently but in the end I went and added every variable manually... and now there is a plugin. Which is great, because all those local variables really cluttered up the sheets visually.

    Anyways, can't wait to try this out, thanks again!

  • Hello Forum,

    I'm still new to game development and Construct 3 and I'm only now learning to appreciate the importance of logging in the console to debug. Does anybody have any effective strategies or practices for logging? Do you just use the basic Browser -> Log action? Do you only add those when you are trying to debug a specific issue in a specific set of events, or do you consistently add logging events to everything? Any cool functions anybody has made to make the logging more robust and easily repeatable across different events? I've been using the CurrentEventSheetName and CurrentEventSheetNumber, but I don't see any way to easily log the Event Group location except by manually typing in the text every time.

  • oosyrag Thanks for the link!

  • Auto picking by newly created objects isn't preserved in a separate same-level event afaik

    I had no idea about that rule. Is there someplace in the manual or a blog that outlines some of these finer details of picking?

    As you said, as soon as I nested the the mirrored condition and Set Flipped action underneath then I didn't need the pick last created.

    But still, the function worked without being nested. Do the picking rules change for Functions maybe?

  • I randomly found a quirk when I was switching an existing function to be a Custom Action. The game is a 2D shooter with the player able to face either left or right and fire projectiles along the horizontal plane.

    The events pictured above, named "spawn_Projectile", both do the exact same thing: spawn a bullet sprite "Dart_Default" on the image point of a weapon sprite, set a damage variable, and then check to see if the Player is mirrored, and mirror the bullet accordingly. The code worked perfectly fine when it was originally inside a function.

    The funny thing is that when I first copied the code from the Function to the Custom Action I got a bug. If the player fires a bullet in the non-mirrored direction (right), then turns and fires a second bullet in the mirrored direction (left), and the first bullet is still on the screen, then the isMirrored toggle will apply to BOTH projectiles, the one just spawned from the barrel of the gun and the one still in flight.

    It caused some pretty cook telekinetic like visuals, where I can instantly change the direction of bullets mid-flight by shooting in the opposite direction and have a whole swarm of bullets bouncing back and forth across the screen.

    Still I had to fix it, and found that adding a "Pick Last Created (Dart_Default)" solved the problem. But why? That Pick Last Created is the only difference between these two events, other than the fact that one is a custom action and one a function. Is there some Picking rule that is unique to either? Does it have to do with the fact that the dart objects were recently created within the event?

  • I see! I haven't gotten much to animation yet, I haven't even used a timeline yet. That makes sense given what I know. And point taken about basing your collisions on an animation that can change size and shape each frame. Thank you.

  • Okay, replied before I really thought about your response. You say collisions are still used without physics, but why would you need a separate base for that? Can't you still have collisions on an animation sprite? Disregarding a fighting game, or any other game where multiple precise hitboxes are critical, couldn't you just create a hit box with Constructs flexible collision geometry? I'm asking specifically in the context of the Construct engine.

  • Thanks for the feedback oosyrag!

  • I've noticed in a lot of sample games I've seen that developers use two sprite objects to represent the player and/or enemies. The "base" is generally a simple square sprite, and the "mask" is the actual character sprite with animations. They are generally pinned to each and/or put in a container together and act as one unit on the layout.

    I understand why you would want this when you are using the Physics plugin (from reading the tutorial), as it allows you to have an animated sprite with a changing shape while still maintaining a simple collision box for the physics.

    However, I still see this done even in games that aren't using Physics, such as the example game Demonaire in the construct recommended examples. Is there another purpose to this practice?

  • Thanks for the explanation!