fisholith's Forum Posts

  • Hey blohod,

    One approach is to place the shadows on their own layer.

    To do this:

    1. Create a new layer above your game.

    2. Set its background color to white.

    3. Set its "Transparent" property to "No", to make it solid.

    4. Place the shadows on this layer, or if the shadows aren't their own objects, just arrange for the shadows to be rendered to this layer.

    5. Set the shadows to be 100% opaque.

    (This way a shadow by itself is black, and two shadows overlapping each other are also black. Everything not in shadow will be white.)

    6. In the layer's properties, under effects, add the "Multiply" effect.

    (You should now be able to see through the layer everywhere the layer was white, and you should see solid black shadows everywhere it was black.)

    7. In the layer properties, set the opacity to something around 30%, so that the shadows won't be solid black. (Unless that's what you want.)

    Finally, depending on how your shadows are cast, you may want to separate your other game objects onto a layer that can receive shadows (below the shadow layer), and a layer that ignores shadows (above the shadow layer.)

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • No problem, glad I could help.

  • Hey alextro,

    You could try setting it up in the following way...

    Have the user specify the number of rows and columns, and store those values in variables named rows and columns.

    Loop: from 1 to ( rows * columns )

    Push ascending integers (use the loop index) into an Array object.

    Here we are basically creating a deck of cards, such that we'll have a card with a unique integer for each cell in the grid.

    Loop: from 1 to columns:

    Loop: from 1 to rows:

    >> Create a text object at ( X = columns * desiredGridSize ) by ( Y = rows * desiredGridSize)

    >> Set local variable RandomElementIndex to: ( floor( random( Array width ) ) )

    >> Set text to: Get Array element ( RandomElementIndex )

    >> Delete element ( RandomElementIndex )

    Notice that each time we pick an element from the array, we also delete it right afterwards.

    This is just like drawing a card at random from a deck of cards, and then discarding the card so it won't show up twice.

  • Hey Crystalline,

    If you're using the "Tile Map" object, it's possible that you have a Tile Map on the game layer, but you don't have a Tile Map on the background layer.

    You'll need a Tile Map object on both layers, if you want to draw tiles on each layer using the Tile Map drawing tools.

    In order to specify which Tile Map object you want to draw on, you can select it before drawing.

    While the Tile Map editing panel is open, if you want to select things you'll need to switch to the normal selection tool (arrow pointer), which you can choose from the Tile Map editing panel's toolbar.

    Notice that the top of the Tile Map editing panel will show the name and UID number of the currently selected Tile Map object.

    Finally, you can hide a layer by clicking the check box beside it's name, to double check that you're drawing on the correct layer.

    Hope that helps.

  • Hey again VictoryX,

    Since it sounds like you're working with objects that are more complex than perfect 1x1x1 cubes, I think what you're looking for is the approach described in the Filmation math article.

    The math can look a bit daunting, but fortunately it's actually not too bad if you break it into parts.

    There are really only 2 main mathy things going on in the article, "Positioning objects on screen" and "Z ordering objects".

    Basics

    Firstly consider that an Isometric world represents a 3D space, with its own X, Y, and Z coords.

    However, your computer screen, (your Construct2 "layout"), is a 2D surface with only X and Y.

    (From here on, the 2D X and Y axes will be referred to as Xp and Yp, to match the article)

    In the article, this image shows the 3D coord space, labeled X, Y, Z (three central arrows),

    and the 2D coord space, labeled Xp, Yp (two arrows at left and bottom. And notice that Yp points up NOT down).

    I'll explain what the "(x0,y0)" is later.

    First super important thing to notice is that the 3D coord axes and 2D coord axes have some similar looking labels,

    X vs Xp, and Y vs Yp,

    but they are NOT the same thing.

    For instance, the isometric 3D Y axis points towards the bottom left of the screen, but the 2D Yp axis points upwards towards the top of the screen.

    With that in mind here come the mathy parts...

    Positioning objects on screen

    Firstly we'll need to decide where the origin of our 3D space (0, 0, 0) will appear, on the 2D screen.

    In the image above, the 3D space origin (0, 0, 0) is in the middle of the image, and it is labeled "(x0, y0)".

    But why is the 3D origin point labeled with 2D coords in that image?

    Well, it's a point in 3D space, but it's also a point on the screen.

    So it would be technically correct to label the point with both 3D and 2D coords, such as (0, 0, 0) & (x0, y0).

    It might not be obvious at first, but the 2D coords (x0, y0), are actually variable names, NOT x times zero or something.

    (x0, y0) means ( The Xp coord of the 3D origin , The Yp coord of the 3D origin ).

    So, if we had a 2D display area that was 600 pixels wide by 400 pixels tall, and we decided to position the 3D origin dead center in that 2D display area, then the 3D space origin (0, 0, 0), would appear at the 2D coords (300, 200).

    So in this example, in the above image, the "(x0, y0)" label, x0 would equal 300, and y0 would equal 200.

    So suppose we put an object, a cat, in our isometric world.

    Our isometric world is a 3D space, so in order to properly place this cat, in 3D space, we need to specify an (X, Y, Z) coord for it.

    Lets make things easy, and put this cat at (0, 0, 0). This places it at the origin of our 3D universe.

    Which is how you keep cats happy.

    So we know where the cat is in the 3D universe, but we don't yet know where it should show up on our 2D screen.

    We don't yet know what the cat's 2D display position is, its (Xp, Yp) coord?

    Okay, so we actually do know where the cat should appear on the screen, in this very special case, because we placed the cat at the 3D origin, and we set the 3D origin to appear at the 2D screen coords (300,200). But we don't yet know how to *CALCULATE*, from the 3D world coords, where the cat should appear in 2D screen coords.

    This is where the first of the mathy parts comes in:

    The article gives us the following formula for converting 3D world coords to 2D screen coords.

    (x,y,z) ? ( x0 + v3 x/2 - v3 y/2 , y0 - x/2 - y/2 + z )

    And before it does anything else, the article says, screw that formula, it's too hard, and doesn't fit pixel art very well, so use this simpler formula instead.

    (x,y,z) ? ( x0 + x - y , y0 - x/2 - y/2 + z )

    The catch is, the simpler formula will squash things vertically (on the 3D Z axis) just a little bit. But that's okay, because now all the diagonal floor tiles will have their seems running along the 3D X and Y axes, making them 2-to-1 pixel diagonal lines, which is much easier to draw in pixel art than 30 degree lines.

    Lets try this simple formula on our cat and see if the result makes sense.

    Recall that x0 and y0 are the 2D screen coords we placed our 3D origin at. In our case,

    x0 = 300

    y0 = 200

    and our cat is at,

    (0, 0, 0)

    (x,y,z) ? ( x0 + x - y , y0 - x/2 - y/2 + z )

    (x,y,z) ? ( 300 + x - y , 200 - x/2 - y/2 + z )

    (0,0,0) ? ( 300 + 0 - 0 , 200 - 0/2 - 0/2 + 0 )

    (0,0,0) ? ( 300 , 200 )

    So our cat should be displayed at 2D screen coords (300, 200), which makes sense, because that's where we placed the 3D origin, and we placed that cat at the 3D origin.

    Now what if we place a meatloaf at 3D coords (0,0,1), directly above the cat in the 3D space Z axis?

    (x,y,z) ? ( x0 + x - y , y0 - x/2 - y/2 + z )

    (x,y,z) ? ( 300 + x - y , 200 - x/2 - y/2 + z )

    (0,0,1) ? ( 300 + 0 - 0 , 200 - 0/2 - 0/2 + 1 )

    (0,0,1) ? ( 300 , 200 - 0 - 0 + 1 )

    (0,0,1) ? ( 300 , 201 )

    So the meatloaf is displayed a little higher on the screen than the cat.

    (Remember that in this article screen Yp points up NOT down. This is not mandatory for isometric graphics or anything, you can tweak the math to make screen Yp point down if you need it to, which you probably will, since Construct2's screen Y axis points down.)

    Z ordering objects

    The second of the two mathy parts.

    In the article, this part begins in the paragraph right after the simplified expression:

    "But there is one detail left: if an object A appears behind B, we must draw A before we render B."

    And this is true.

    So if object A and B are rectangular solids, and aren't necessarily cubes we need to know where they are and how big they are in 3D space.

    For each of these two objects we can describe their position and size with two points.

    How can we describe a 3D cuboid with two points?

    Well, consider when you are selecting a bunch of files on your desktop, and you click and drag a rectangular selection box.

    The start point you clicked and the end point you dragged to are two points that describe the size and position of that selection rectangle.

    Similarly, you can describe 3D rectangular solids by specifying 2 points, like a start and end point for a 3D selection box.

    For instance, imagine drawing a rectangular selection box flat on a desk, but then dragging the end point up off the desk, to raise a rectangular prism up out of the desk surface, like a sky scraper.

    In the article, object A is described by two points expressed as (xA,yA,zA) and (x'A,y'A,z'A).

    Notice the single quote marks in the second point, these single quotes are usually read as "prime", so that x'A would be read as "x prime A". Basically it's just a way of distinguishing starting x from ending x', that's all.

    Anyway, object B, as you might guess, is expressed as (xB,yB,zB), (x'B,y'B,z'B).

    Finally, the article tells us that the start point must be the far vertex on the cuboid, and the end point is the near point on the cuboid.

    Though the article puts it in mathematical terms as seen below.

    xA < x'A,

    yA < y'A,

    zA < z'A.

    All that's left is the actual calculation to determine if two objects overlap, and then if they do, which is on top.

    At this stage the process basically is just some greater-than, less-than style math, so the hard part (conceptually following what all the coordinates represent) is pretty much already out of the way.

    I hope that helps out.

    Sorry for the monstrous post.

  • Hey VictoryX, <img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile">

    Filmation math

    http://bannalia.blogspot.de/2008/02/filmation-math.html

    This is one of the best explanations I've found for a general approach to Z ordering objects for isometric rendering.

    Isometric depth sorting

    https://mazebert.com/2013/04/18/isometr ... h-sorting/

    This dev blog article walks through implementing and optimizing isometric Z ordering for a game targeted at mobile devices.

    It actually links to the first article, but this one spends more time talking about the actual code, pitfalls, and making it efficient.

    My isometric demo capx

    https://www.scirra.com/forum/viewtopic.php?f=147&t=127352&p=900336#p900336

    (see my 1st and 3rd post for details. 3rd post has capx.)

    Finally, here's a reply post I made a little bit ago, for some one asking about jumping in isometric games.

    I built a simple isometric demo capx, which you can download there.

    Granted I'm using a simpler Z ordering system than is described in the above articles.

    Example image:

  • Thanks for the reply Rex,

    I looked over the Function runtime code.

    So it looks like "AddVariadicParams()" is passed as an array to the corresponding runtime function single parameter.

    Is it okay to use it for Plugins and Behaviors?

    Ashley mentioned in an older post that it was a hack for the Function object, and was not recommended for use in 3rd party plugins. Granted, he said that was not recommended, 'for the time being', and that was back in 2012.

    Do you know off hand if whatever limitations that existed then are no longer an issue?

    The plugin I'm working on would ideally be usable as a behavior.

  • Is there an official way to make Conditions and Actions with unlimited params (variadic)?

    I know Expressions have the "ef_variadic_parameters" flag.

    And the Function object uses the "AddVariadicParams()" method for the "Call Function" Action, but I'm not sure if this method is for the Function object only, or if it's okay for general use in 3rd-party plugins.

    So, I'm curious if there is an official way to do this for Conditions and Actions.

    Thanks in advance for any info or suggestions.

  • Hey all,

    Does anyone know where in the C2 runtime shader Effects are applied?

    In particular I'm trying to find the code which sets up each effect's coordinate space, for an object with a list of applied effects.

    That is, inside the GLSL code, the X and Y sample coords are different from layout or canvas coords.

    I'm trying to find the code that does the canvas-to-GLSL coord space conversion.

    Granted, it might be that the effects are just called on some kind of quad, and the coords are taken from the quad corners, I'm not sure how exactly GLSL coords work in C2.

    Basically where ever the GLSL coord space is explicitly or implicitly determined is what I'm trying to find.

  • Thanks Rex and Ashley for the info

    I looked through the example code you posted, Rex,

    and in the context of some of the things you said, Ashley,

    I think I kind of see what's going on.

    Is there a place in any of the runtime files I could look to see the implementation of the SOL system?

    In particular I'd be interested to see a list of the available methods and fields you can use to interact with the SOL.

    Kind of the SOL "interface".

    Is a SOL attached to each C2 object type, and implemented as a SOL JavaScript object, assigned to a JavaScript field of the c2 object type?

    Or is it something global to the entire runtime?

    I might be misremembering, but it seems like I've been seeing things that looked to me like they could indicate that it's handled on a per C2 object type basis.

    I haven't yet been able to track the references to the SOL far enough back upstream to see where they are originating from.

    I also gather that there is a SOL stack that is used to store (for later restoration) SOL state as you recurs into deeper levels of events. Though again, I'm having trouble finding the actual code that would make it clear if that stack is a field of a C2 object type. I suspect that it might be in the commonace.js file, but I haven't been able to spend much time poking around in it yet.

    Sorry for the noobly questions.

  • Is there a good place or plugin to look at to learn how to implement object picking?

    I've been looking around a bit, and it's hard to tell what is and is not special-case picking code.

    (Kind of the same way the function object has the "fast trigger" system that's not intended for general SDK use.)

    Does anyone know of an SDK picking tutorial, or a plugin that has generally applicable picking code?

    Part of the reason I'm interested is that I'm building a document full of template code snippets for various common ACE related tasks, such as looping, executing triggered conditions, and now picking and SOL manipulation, if I can figure it out.

    Any thoughts or advice much appreciated, thanks.

  • Hey Thndr, <img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile">

    You might be able to solve this by using "Families" to escape the reciprocal picking that happens with containers.

    Manual > Families: https://www.scirra.com/manual/142/families

    Setup:

    Place the person into a family named "Damageable".

    Event:

    Hitbox is attacking. // Selects all attacking Hitbox objects.

    For each hitbox.

    Event:

    Hitbox is overlapping Damageable family

    Pseron.UID does NOT equal Damageable.UID // Prevents the Hitbox from attacking the Person who owns it.

    Actions:

    Damage Damageable. // Deals damage to all Persons overlapping the Hitbox who are not the Hitbox owner.

    Note the sub event condition that includes Person.UID.

    Here we are assuming that the Hitbox's Person is selected because they are both in the same container, and we're also hoping that won't influence the selected objects in the Damageable family, and it shouldn't, BUT...

    If you want to be absolutely sure that the containers aren't involved, and thus can't get up to any of their containerly shenanigans, then instead of getting Person.UID from the Person implicitly selected along with the Hitbox, you can store the Person UID directly in the corresponding Hitbox when the Hitbox is created.

    Event:

    Hitbox, On created:

    Actions:

    Set Hitbox private variable "parentPersonUID" to Person.UID.

  • Hey unska, <img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile">

    Construct 2 has a feature called "Families" which might help out in this case.

    Manual > Families: https://www.scirra.com/manual/142/families

    You can put several objects in a family, then write events that apply to the family , and those events will affect all the family member objects.

    You can also add private variables to a family, and all family member objects will get their own local copies of those variables. This could be handy for the properties common to all monsters, speed, bullets, health, sprite, name, loot drop, and such.

    As for collisions and graphics, for a single character I usually create two separate dedicated objects. One for collisions, physics, and motion, and one for the graphical representation of the object in the game. The physics object will be invisible. The graphics object will be pinned to the physics object. This way my collision geometry can be very simple and predictable, and my graphics can be modified without affecting the collision properties of the character.

  • Hey farflamex,

    I'm not sure if this is what you're looking for, but the Browser object has expressions for screen width and height, as well as a "Request fullscreen" action.

  • You could try using the "overlapping at offset" condition of the Sprite object to test to see if it *will* be overlapping a wall if it makes a given move. You can use that information to cancel the move if it would result in overlapping a wall.

    The "overlapping at offset" condition basically ghost moves your object temporarily to a nearby offset, where it checks for an overlap, and afterwards returns the object to it's real location.