fisholith's Forum Posts

  • No worries, perfectly legitimate question.

    Multiply blend is one of several "Effects" you can add to objects or layers.

    There are two ways to add an effect to an object:

    • Project panel: In the project panel, in the object folder, right-click the Tilemap object, and choose "Effects..."
    • Property panel: In the layout, select the Tilemap object, and in the property panel, in the "Effects" section, click the blue "Effects" link.

    Once you have the Effect window open, click the plus

    +

    button to add an new effect.

    The effect list should open. In the "Blend" category choose "Multiply".

    And that should do it.

    If you use multiply to overlay color blocks on the ANSI character grid, you'll probably want the ANSI tiles to be white characters on a black background. This way after multiplying them they will be colored characters on a black background.

    The Multiply blend is going to "multiply" color tile RGB values into the ANSI tile RGB values.

    For blending purposes, the RGB values of pixels are treated as ranging from 0.0 (black) to 1.0 (Max).

    e.g.

    White is RGB( 1 , 1 , 1 ).

    Black is RGB( 0 , 0 , 0 ).

    Red is RGB( 1 , 0 , 0 ).

    Gray is RGB( 0.5 , 0.5 , 0.5 ).

    So if you multiply white (1,1,1) by any other color, the result is that other color.

    e.g. (Note: Here I'm using the font-color to differentiate the first color from the second color, rather than to show RGB channel color.)

    white (1,1,1) * red (1,0,0)

    = (1*1 , 0*1 , 0*1)

    which = (1 , 0 , 0)

    which is red.

    Likewise, if you multiply black (0,0,0) with any color, the result is always black, because each channel gets multiplied by 0.

    So if you think about it, whenever you multiply two colors together, the resulting RGB values for a given pixel will never be brighter than the input values, as you'll always be multiplying values in the 0 to 1 range.

    Practically speaking, the effect of multiply blend is like projecting a colored light onto a colored texture.

    Projecting white light onto a colored texture just shows the texture.

    Projecting "black" light (no light) results in solid blackness, for obvious reasons.

    Projecting red light results in green and blue features not being visible, as they'll have an intensity of zero. Essentially, what you'd expect from viewing the texture under red light.

    Side note, for some reason Construct 2's text objects look kind of weird if you add any blend-with-background type effects to your game, but only in the editor. They'll still look fine at runtime.

  • No problem, glad to hear it worked out.

  • Hey zardaloop,

    As far as I know, if you have events responding to user interaction in the hidden layer, the event's will continue to run normally whether the layer is invisible or not.

    To disable interaction with that layer, you could track down the relevant events relating to that layer, and alter them, so they include a "Layer is visible" condition.

    That way when the layer is invisible, none of the events relevant to that layer will run.

    If there are a lot of events specific to that layer, for instance if it's a show/hide-able menu, you can put all the layer's events inside a group, and toggle the entire event group on and off when you show or hide the layer.

  • One approach that might work is to have two tileset objects, one for ANSI characters and one for color masks.

    You could then multiply-blend the color mask tileset over the character tileset.

    That would allow you to change the character or the color independently, without needing a unique tile for each combination of character and color.

  • Glad I could help out.

    I built an example as well, though it only shows the process of calculating the unwrapped angle.

    I'm afraid it doesn't apply it to a gloriously cucumber rotating cause.

    [attachment=0:2r12h9lh][/attachment:2r12h9lh]

    I checked out the cucumber example. Very cool, as one might expect a cucumber to be.

    Using System > Wait to time travel

    One way to compare the current unwrapedAngle against the 2 seconds old unwrapedAngle is to:

    • Create a custom variable "unwrapedAngleOld".
    • Every tick: System > Wait 2 seconds; and Set unwrapedAngleOld to the current unwrapedAngl value,

    That should make unwrapedAngleOld continuously play back the unwrapedAngle values from 2 seconds ago.

    I have no idea how optimal this is, but I've never seen a performance hit. Granted I'm using a desktop, and it might be a different story on a mobile device.

    Angle delta math

    As for the mathematics behind the angle_delta formula from my previous post, I can hopefully simplify it a bit.

    In my prior post I wrote it out as:

    angle_delta = ( ( ( a - b ) + 180 ) - floor( ( ( a - b ) + 180 ) / 360 ) * 360 ) - 180

    The reason it looks so awful is that the formula contains a mod() function, which I've written out long hand.

    Here is the formula as it appears if you have an appropriate mod() function available:

    mod( ( a , b ) + 180 , 360 ) - 180

    Unfortunatly you don't have the appropriate mod() function in C2 by default, and that's why I wrote it out in expanded ugly form.

    (Further explained in "C2's modulus" section below.)

    That mod formula I'm using above has the following behavior:

    mod( val , div ) ... = ... val - ( floor( val / div ) * div )

    This is a "floored division" style mod.

    Terrible as it might sound, there are a few different versions of the mod formula, all called the same thing, but all slightly different.

    There's a nice chart showing mod variations on the modulus wiki page.

    C2's modulus

    Now, C2 does provide a mod function in the form of the modulus operator "%", and this is essentially just JavaScript's modulus operator.

    Unfortunately not all mod functions work the same way, and JavaScript's built-in mod is a variety that behaves differently depending on whether the val number is positive or negative.

    This will not work for our purposes because we want consistent behavior for both negative and positive numbers. That means JavaScript's mod, and by extension C2's mod can't be used here.

    (I'm actually building my own math utility plugin right now, and I was initially inspired by the lack of a "floored division" mod.)

    Getting "floored division" mod into C2

    Granted the formula does look a lot nicer with the mod(), and it would be nice to be able to use it in C2.

    There is a way, and I show it in the example capx I attached.

    You can use C2's Function object to create a custom "floored division" style mod function.

    • Create an "On function" event with the function name "mod".
    • Add the action "Set return value", and in the expression, use the "floored division" mod formula I described above.
    • Within the expression you'll need to use Function.Param(0) and Function.Param(1) to get the arguments. (Or you'll need to do what I did in the example capx: Store the args in local variables, and use those in the expression, which may be easier to read.)
    • You can then call this custom mod function from another C2 expression.

    Again, you can see this setup in the example capx if you're interested. It does make the angle_delta function much nicer to look at.

    Note that I renamed the Function object from "Function" to "oF", which just makes it easier for me to read expressions that involve the function object.

    I hope that helps clarify some of the stuff I explained a bit hastily in my prior post.

  • Hey gwerneck,

    (Edit) I just checked the System >> "angleDiff" expression, and it always gives a zero or positive answer, meaning it doesn't give you the direction of the change in the angle, so it won't work here.

    One possible approach is to calculate the change in angle between ticks,

    store that change in a running total,

    and track that running total instead of tracking the objects rotation property directly.

    Here is a formula that will give you the change in angle as the shortest CW or CCW rotation, and it works seamlessly across the 360-to-0 transition.

    angle_delta = ( ( ( a - b ) + 180 ) - floor( ( ( a - b ) + 180 ) / 360 ) * 360 ) - 180

    Where a and b are your two angles.

    e.g.

    a = 5, b = 355: ... ( ( ( 5 - 355 ) + 180 ) - floor( ( ( 5 - 355 ) + 180 ) / 360 ) * 360 ) - 180 355: ... = 10

    a = 355, b = 5: ... ( ( ( 355 - 5 ) + 180 ) - floor( ( ( 355 - 5 ) + 180 ) / 360 ) * 360 ) - 180 355: ... = -10

    Remember, if you compare two angles with a difference greater than 180 degrees, the angles will be treated as a shorter rotation in the opposite direction.

    e.g. A raw difference of +270 is treated as -90.

    As long as the total distance rotated per tick is less than 180 degrees that shouldn't be a problem though.

    So, to use this in place of directly tracking the object's angle property, you can do the following:

    Create a custom variable "unwrapped_angle".

    Every tick, get the change in angle between ticks, angle_delta( currentAngle , angleRecordedLastTick ), and add it to "unwrapped_angle".

    Then you should be able to use the unwrapped_angle in place of the objects built-in angle property.

  • I tested out the capx in FF, Chrome, NodeWebkit, and IE.

    I seem to get the same additive blending effect in FF.

    Below is an image showing screenshot comparisons of the four platforms I tested.

    I changed the background image to a stone texture with a sharply defined edge, to make the FireFox blending behavior easier to see.

    [attachment=0:1iybqo88][/attachment:1iybqo88]

    At the far right is a recreation of the FF example composed entirely in Photoshop, with the Logo set to additive (Linear Dodge) blend, and overlapping the stone. I lined up the recreation to match the FF example, down to the pixel, and the resulting Photoshop recreation is exactly identical to the FF version.

    So it looks like FF really is using the additive blending mode for some reason.

    FF: v35.0

    Chrome: v40.0.2214.93 m

    Hope this helps out.

  • Problem Description

    When previewing in NodeWebkit,

    and receiving C2's "Cannot create an instance" error message,

    the bottom line of the paragraph is chopped off.

    The popup appears in the form of a Windows alert popup, (I'm using Windows 7)

    This bug only seems to occur in NodeWebkit. When previewing in Firefox or Chrome, the entire text is fully readable and is NOT chopped.

    The image below shows how the error popup (top of image) appears on my computer.

    [attachment=0:3rxed8ev]No instances error.png[/attachment:3rxed8ev]

    Also seen in the image, is a program called GetWindowText (bottom of image) which I used to extract the popup text so I could read the entire message. That was before I realized that the chopping issue seems to only occur in NodeWebkit.

    Attach a Capx

    [attachment=1:3rxed8ev]bug - NodeWebkit - No instances error.capx[/attachment:3rxed8ev]

    Description of Capx

    When you run the capx, it should preview in NodeWebkit and display the chopped error message popup described above.

    (This capx was created by following the "Steps to Reproduce Bug" instructions I included below.

    Except I also drew a little face on the sprite, which is not an included step. <img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile">)

    Steps to Reproduce Bug

    • 1. Create a new project.
    • 2. In the project properties, in the "Configuration Settings" property group, set the "Preview browser" option to "Node-Webkit".
    • 3. In the layout, create and place a new sprite object.
    • 4. Remove the sprite object from the layout, such that the sprite remains in the project's "Object Types" folder, but so that no instances of the object exist in the layout. (You should get a message explaining that you have deleted the last "sprite" instance.)
    • 5. In the event editor, create a new event, with the condition "On start of layout", and the action "Create object sprite on layer 0 at (0,0)".
    • 6. Run a preview of the layout.
    • 7. You should see an error message appear in the form of a Windows alert popup. (I'm using Win7) The bottom line of the text is chopped off. On my computer the last readable words are "To resolve this add at least one".

    Observed Result

    The text of the error message is chopped off at the bottom, and so the last part of the message is missing.

    On my computer the last readable words are "To resolve this add at least one".

    Expected Result

    A popup with the entirety of the message visible.

    The end of the text message, when fully visible, normally reads "To resolve this, add at least one instance of the object to the project, on an unused layout if necessary."

    Affected Browsers

    • NodeWebkit: YES
    • Chrome: NO
    • FireFox: NO
    • Internet Explorer: NO

    Operating System and Service Pack

    Win7 x64 - Pro

    SP1

    Construct 2 Version ID

    Release 195 (64-bit)

    Built at 15:36:00 on Jan 19 2015

    Release notes link: http://www.scirra.com/construct2/releases/r195

  • No problem, glad to help out.

  • Hey thorntonp72,

    The "Collision" condition for objects "A" and "B" will only trigger when object "A" transitions from a state of not overlapping "B" to a state of overlapping "B".

    The "Overlapping" condition will be true whenever "A" overlaps "B". (e.g. If you put this condition in a top-level event by itself, it will run every tick.)

    Collision is a "triggered" condition, while Overlapping is a "true/false" condition. In fact the little green arrow just to the left of the collision condition is to indicate that it's "triggered" instead of "true/false". Triggered conditions don't get checked every tick, instead they just execute instantly when their criteria are met.

    (See the header "Events run top to bottom" in this article for more about the difference between the two condition types.)

    There are a few ways to get the continuous damage effect...

    Smooth damage over time

    You could have the lake deal a very small amount of damage every tick, (every frame-draw, about 60 times a second). This would cause the health to drain smoothly while in the lake. This is probably the simplest to program. You can also optionally use decimal damage values less than 1.0, and round the hp number when displaying total hp.

    Chunk damage every second

    You can have an event check every second to see if the player is overlapping the lake, and if so deal damage to the player. If you want the damage to come in chunks, this will do it, but this particular method is not such a good approach to chunk damage.

    If a player steps in the lake right after a 1-second-check, and the player gets out right before the next 1-second-check, they'll take no damage. Even if they don't get out, it could take nearly an entire second before they take damage, which could look weird.

    Chunk damage every second of exposure

    This is probably the more elegant way to deal chunk damage. You deal a chunk of damage when the player *Collides* (not overlaps) with the lake, and you start a 1-second timer, (using the timer behavior). When the timer triggers, you check to see if the player is still overlapping the lake, and if they are, you deal a chunk of damage, and start the time again.

    If a player jumps in the lake they will instantly take damage, and every second afterwards if they stay in the lake, that 1-second time will deal damage and reset, over and over.

  • Hey heater19,

    I made a modified version that does what you're looking for I think.

    I included comments to explain what I changed and what's going on in the event sheet.

    [attachment=0:1dmfogcj][/attachment:1dmfogcj]

    Problem

    I'll use "Block" to refer to the green blocks, instead of "Sprite", to stay consistent with my example capx.

    (I renamed the object in the capx while I was trying to figure things out.)

    I think what was happening in the original capx is that the "While Block is overlapping Block" event was not actually selecting (picking) any blocks.

    The reason is that this "While" event was a sub event, attached to a parent event in which you created a new block using "Create object".

    After creating a new Block with the "Create object" action, only that new block is selected. When you enter a sub event, it's still only that one Block that's selected.

    So, when the sub event tries to check for overlapping Block objects, it is only checking for overlaps among the selected blocks. The problem is there's only one selected block at this point, and it can't overlap itself, so no Blocks meet the overlap condition, and as a result now no Blocks are selected.

    From this point on, there are no Blocks selected, so any events acting on Blocks will have no effect.

    You can test this by locating the "While" event, and adding the action "Block: Set Scale to 4", which will make any Blocks selected by that overlap condition 4 times bigger. When testing you should still see overlaps, but no Blocks will ever be made bigger, because that event never actually selects any Blocks.

    Workaround

    It turns out that handling collisions between two instances of the same type of object, and then acting on one of those instances is a little tricky in Construct.

    To get around this, we put the Block object in two different families "fS_overlapA" and "fS_overlapB".

    (The "fS_" is just my shorthand for "Family" of "Sprite" type objects.)

    A family keeps its own separate list of selected (picked) objects.

    So the event

    While

    Block overlapping Block

    can now be rewritten

    While

    Pick fS_overlapA by UID (use current Block's UID)

    When fS_overlapA overlapping fS_overlapB

    This guarantees that one of the Blocks in the overlap will be the current Block, and the other Block(s) will be selected from the entire set of existing blocks.

    We then call a custom function that takes the current Block's UID as an argument.

    That function picks the current Block (via the UID we just passed it) and repositions the Block randomly.

    I only created a function to do the placement because it means we can call the function once when first placing a Block, and again if there's a Block overlap. So, to change how the blocks are randomly positioned, you only have to change the code in one place.

    Also, I'm not sure if this was intentional, but in the original capx, the random position ranges start at "32" when creating the Block, but they start at "64" when repositioning a Block after an overlap. In the function based version in the capx I attached, the ranges will always start at "32".

    Hope that helps out. :)

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • Ah, okay. I think it may be the use of the "Physics" behavior.

    I had been thinking you were using a home-made physics system that just did force, velocity and movement. This would mean that you could move an object each iteration of the loop as part of the update process. By contrast, the Physics behavior doesn't expose any kind of "update on command" functionality, to my knowledge.

    Here's a deeper explanation of what I think is going on:

    By default, C2's Physics behavior is handled by the Box2D physics engine.

    (I'll refer to the Physics behavior as "Box2D" from here on, though C2 actually allows other engines as well.)

    If I recall correctly, loosely, the way Box2D works in C2 is that, you tell Box2D you want to apply forces to an object, and Box2D will update (move) the object for you. Box2D is hard-coded to do that update after all your events, but before the next tick.

    That right there is the problem.

    You can't force Box2D to do multiple updates in a single tick. The C2 plugin doesn't give you the ability to force Box2D to update on command.

    So, inside a loop, if you add a Box2D force to the ghost, for 100 iterations, it will just pile up the applied forces on each other into a larger and larger single force.

    But Box2D won't update the ghost's position at all yet.

    So each iteration, the ghost is still in the same place, and all the prediction path dots show up in the same place.

    The problem is, Box2D will wait for the loop to end, and it will wait for all your other events to end, and then before the next tick, it will apply that one massive 100X force to the ghost, moving it to who knows where.

    (It also doesn't matter where, because the ghost's position will be wrong, and it will also get reset next tick.)

    It is theoretically possible to get this path prediction system working with Box2D, if the Physics behavior plugin was modified to provide an "update on command" action, but without being able to force an update, you can't use Box2D to move the ghost on each iteration of the loop.

    Sadly, this means that, other things being equal, this prediction method just won't work with Box2D.

    One possibility, (that doesn't involve modifying the Physics behavior plugin), is to recreate, in C2 events, the same update formula that Box2D uses. You can then use that to move the ghost ship, each iteration.

    Fortunately for your purposes that update formula is probably the simplest part of Box2D to recreate. Basically just, apply force to an object, determine the new velocity, and move the object based on the velocity.

    I think by default Box2D uses a frame-rate-dependant mode, so if you know what math Box2D does on each tick, you should be able to exactly recreate it.

    As not-so-fun as that might sound, I think it's probably what I would try if I were in the same situation.

    One tricky part may be figuring out what Box2D thinks the ship's mass is, because I don't think there's any way to get that info from C2 in the correct units. Though I have seen a few posts on the C2 forums talking about the pixel-to-mass conversion that C2's Box2D uses. I think it was either R0J0hound or Ashley I saw talking about it.

    I wish I could offer more help, but I'm not that familiar with Box2D's internals, or how precisely it's integration into C2 affects the behind-the-scenes math.

    If I think of something else, I'll let you know.

    Best of luck. :)

  • Hi Mathijs90,

    One way you might approach this is with a custom variable to represent the turning rate.

    I'll call this variable "turnRate".

    You can think of this variable as keeping track of the steering wheel position, with "0" meaning centered, positive numbers steering right-wards, and negative numbers steering left-wards.

    The events that allow the player to control this variable should do the following:

    When you hold RightArrow the turnRate value will gradually increase up to a cap value (e.g. +300),

    and when you hold LeftArrow the turnRate value will gradually decrease down to a cap value (e.g. -300).

    When you're not holding any keys, the turnRate value will gradually move towards 0.

    To get the Car behavior to use this variable for steering, you'll first need to disable "Default Controls".

    In the layout, select the car, and in the car's properties, in the section Behaviors > Car, find "Default Controls", and set it to "No".

    This disables the automatic key bindings that control the car.

    Don't worry, you can manually recreate these key bindings with events, using the car's "Simulate Control" action.

    Recreate the key bindings for acceleration and breaking as follows:

    UpArrow is held down: For Car: Simulate Control - Accelerate.

    DownArrow is held down: For Car: Simulate Control - Break.

    To link the turnRate variable to the car's steering create the following events:

    Every tick: For Car: Set steer speed to turnRate.

    Every tick: For Car: Simulate Control - Steer right.

    It looks weird, but what's going on is that the car now always thinks it's steering to the right, but by the amount stored in the turnRate variable.

    When turnRate is positive, the car will turn right.

    When turnRate is negative the car will turn left. (a "negative" right turn)

    And when turnRate is 0 the car will go straight.

    As a final thought, you may want to make the turnRate return to 0 much faster than it gradually climbs away from 0, i.e. when turning left or right. Even though realistically, a human driver really would have to turn the steering wheel back to center manually, for a game, the controls will probably feel more responsive if the return to center occurs in a fraction of a second.

    This rapid return to center should obviously take effect when a player is not holding a turning key, but it should also occur when a player is holding a turning key that is currently turning the wheels back to center. In that latter case, once the wheels get back to center, (and the key is now turning the wheels away from center), the steering can go back to being more gradual.

    Hope that helps.

  • Hm...

    I suspect one of two things is happening.

    A: The ghost ship is not actually being updated inside the loop.

    B: The ghost ship is being updated inside the loop, but to the same spot 100 times.

    If the buggy ghost ship is always exactly on top of the real ship, then it's likely that it's situation "A".

    If the buggy ghost ship is always exactly 1 time-step ahead of your real ship, then it may be situation "B".

    The most likely cause of situation B is that, inside the loop, each loop iteration is setting the ghost.state to

    updateFormula( RealShip.state ),

    instead of updateFormula( Ghost.state ).

    It's hard to say exactly without seeing the events though.

    If you can post a screen grab of the events in question, I might be able to figure it out from that.

    (In the event sheet, if you select a bunch of events and right-click on the very left edge of an event block, one of the menu options should be "Screenshot Selection.")

  • Hey again McDonald,

    Sorry, my explanation, may have been a bit vague in places.

    So, here's what should happen in a single tick:

    [TICK START]

    // First we advance the RealShip by one time-step.

    Every tick: RealShip.state = updateFormula( RealShip.state )

    // The ship's "state" information is its XY position, and XY velocity.

    // Given the current "state", the "updateFormula" computes the new state for the next time-step.

    // // I'm using updateFormula as a shorthand here, in your actual code it may be a block of two or more events.

    // Now we prepare the ghost ship to execute a simulation of several future time steps.

    // First we set the ghost ship to match the real ship's state, (XY position, and XY velocity).

    Every tick: Ghost.state = RealShip.state

    // Now we're ready to start the simulation.

    // We'll run 100 iterations, and with each, the ghost will advance 1 time-step into the future.

    // (Note that the tick has not ended yet.)

    Loop from 1 to 100:

    // We update the Ghost, just as we would update the real ship.

    // Critically, we feed the Ghost's state to the updateFormula, and NOT the RealShip's state.

    [sub] > (always): Ghost.state = updateFormula( Ghost.state )

    [sub] > (always): Ghost spawns a dot object.

    // The ghost has now plopped down 100 dots.

    [TICK END]

    Essentially, you'll be triggering the same time-advancing code that Construct would trigger each tick (frame-draw), but you're not waiting for Construct. You're manually triggering the code 100 times before the tick ends and the frame is drawn. You're just doing it with a ghost copy of your ship.

    As for working with player controls, when the simulation is updating the ghost ship, you can have each loop iteration update the ghost as if the player is continuously holding down the same controls that were used when updating the real ship.

    Hope that helps out.

    Variation

    As an alternative variant of the above approach, you could conceivably run the simulation with your real ship, and not even bother using a ghost ship.

    You would need to store the state of the real ship after its update, but before starting the simulation, so that you could recall the real ship back to its proper pre-sim state, before executing any remaining code in the tick.

    Thus, even if during the simulation the real ship collides with a lethal object, you'll still be inside the sim loop, and none of your collision handling or ship-exploding code will be running yet. So as long as you put the real ship back, by the time your collision handling code runs, it will never know the ship "time traveled" into a future hazard.

    ... And witnessed its own death! D:

    Also, dang it R0J0hound, how are you always using a newer version of construct than me.

    I swear, I learn about C2 updates from clicking on your examples.

    I probably need to enable beta updates, huh.