WackyToaster's Forum Posts

  • I hope Ashley can chime in here I don't wanna put it as a bugreport because it could be by design.

    In Events:

    If I start a tween (2seconds) that I then stop after 1 seconds. The tween just stops.

    On Tween "Tag" finished does NOT trigger.

    In Script:

    class MySprite extends ISpriteInstance {
    	constructor() {
    		super();
    	}
    
    	async start() {
    		this.tween = this.behaviors.Tween.startTween("x", this.x+200, 2, "linear");
    		this.tween.finished.then(() => {
    			this.setAnimation("Animation 2")
    		})
    	}
    
    	cancel() {
    		if(this.tween.stop) this.tween.stop();
    	}
    }

    I do essentially the same. However, this.tween.finished.then() still runs anyway (right when the tween is stopped). My thinking was that if the Tween is stopped, I would not consider it finished. It's stopped.

    I guess the solution would be to track an extra variable that shows that the tween was stopped and not simply finished. It would be much more convenient though if at least I could get "was the tween stopped" from the ITweenState somehow, however, ITweenState becomes effectively unusable after stop.

    So is this by design, a bug, a feature?

  • Hmm that's actually a tough issue given that the default of sorting at the Y position is not gonna cut it.

    Perhaps the easiest solution for the fences specifically would be to split it up into individual sprites (and use hierarchies to stich them back together), so that sorting by Y position would approximate more closely to what you'd expect.

    I can imagine there could be a more involved solution where you figure out the draw order based on some hidden top-down representation of the scene.

    Or it's possible that you utilze the 3D capabilities of Construct. If you imagine the scene as isometric 3D, the fence would be on the side of the cube object, and the sorting is handled by the 3D magic.

  • Yeah it is missing. For the time being, you will have to make a function with the events and use runtime.callFunction()

  • Would be good if you can post your project in the bugtracker.

    github.com/Scirra/Construct-bugs

    You can probably remove everything except the bugged layer that's causing the problem.

  • ive never used flowcharts-are there for java script only or can u use it with events?

    They are currently event-only. Whether you should use them depends on what you want to do. If you do a text-adventure or something, they are great.

  • Yes, we have plans for leaderboards to work independently of the Construct Arcade.

    Interesting. Does that mean you plan on hosting a server/service for the leaderboards? And potentially more?

  • Load and save what?

    For me I was talking about the data of the flowchart and/or individual nodes. I don't think that that the feature is abandoned but this thread was just kind of fitting to me since I recently tinkered with them a little. And I thought about submitting a feature request but I just didn't have the urge to do so yet, it's not a pressing issue for me.

  • I'm personally still waiting for the scripting API. I kind of expected that to come sooner rather than later, but it seems to be later... Save and load from JSON would also be great. Right now when I tinker with it I basically write a parser in the eventsheet that outputs the node as a JSON. :V

  • The settings going away is probably a browser thing, like if you delete browser history. But I did notice that you will be logged out every time if you log in on another device like a Laptop or something.

  • Use hierarchies instead of pin.

  • Oh yeah, I'm on the beta branch. You can just open it in the newest beta if you want to take a look.

  • You are WAY overcomplicating things. All you need to do is...

    wackytoaster.at/parachute/cardflip.c3p

    At least that's roughly how I'd do it. But there's obviously more than one way to get to the goal. I've used something similar to your method before for something else, where I stored the UID in a tag.

  • Alright final update for now. I've updated the project (same link)

    wackytoaster.at/parachute/ceilingSlopes.c3p

    I did add a velocity-based pushout that solves issue #2 mentioned above, but adds some other issues in that it sporadically does not work. So it's disabled in the project. It's kinda fun though.

    Issue #1 cannot be solved as easily as expected, the issue is not that the jump isn't executed by the behavior, but rather that the pushout actually pushes the player into the floor and thus the pushout fails. I tried changing the players height slightly during the pushout to compensate but that also did weird things. I feel like this should work though, maybe I made an error somewhere.

    Issue #3 is a bit more complex than expected, probably another can of worms that I don't wanna open at this moment. I had a solution that kind of worked but was framerate dependent, so... nah.

    For now I'm actually fine with all these 3 issues being present (for my project). It's not make or break for me. But of course, if someone happens to find a solution for them, let me know.

  • Try Construct 3

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

    Try Now Construct 3 users don't see these ads
  • This is an unfortunate and kinda known issue of box2D (and other physics engines too). The only surefire way to prevent this is to make the collision a single, continous solid object (e.g. an invisible tiled background).

  • Ok so after checking the code again I did realize I was indeed doing all kinds of nonsense that somehow worked out lol. I revised the code, now using two raycasts and some checks and I got the jittering under control. Actually it works really well overall now, (almost) no jank and framerate independent. Thanks for the input r0j0

    Three issues remain that I think I can fix in due time.

    1. If the player jumps when wedged under a slope, the behavior will stop the player from executing the jump before my code runs. That should be a somewhat easy fix.

    2. The slopes allow the player to combine the behaviors vectorX with whatever vector the pushout creates, causing the player to exceed the usual maximum horizontal speed. Not 100% certain how I can counteract that yet. I'm thinking something like calculating the vectorX of the pushout and subtracting that from the vector of the behavior. Haven't tested that yet though.

    3. Rubbing against a slope stops the player rather than sliding along it. Not sure how yet but it should be reasonably easy to work around I think.

    Code for V2 to just replace all of player.js

    import * as Util from "./util.js";
    
    export class Player extends ISpriteInstance {
    	
    	#preTick = () => this.preTick();
    	#postTick = () => this.postTick();
    	
    	constructor() {
    		super();
    		runtime.addEventListener("pretick", this.#preTick);
    		runtime.addEventListener("tick2", this.#postTick);
    
    		this.storedY = 0;
    		this.didPushout = false;
    	}
    
    	preTick() {
    		// this ticks BEFORE behaviors
    		// TODO: Ideally I should have an input check here to see if a jump was executed because if the player is wedged under a sloped ceiling, the behavior will set vectorY to 0 and bonk the player before the slope handling kicks in
    		this.handleCeilingSlopes();
    	}
    
    	postTick() {
    		// this ticks AFTER behaviors
    		// if a pushout happened this tick, set the players vectorY to the stored vectorY. This is needed because the behavior will set the vectorY to 0 because it detected a ceiling
    		if(this.didPushout) this.behaviors.Platform.vectorY = this.storedY+this.behaviors.Platform.gravity*runtime.dt;
    	}
    
    	handleCeilingSlopes() {
    		this.didPushout = false;
    
    		// Player is not moving upwards, return
    		if (this.behaviors.Platform.vectorY >= 0) return;
    		
    		// Store current position and expected next position based on velocity
    		const old = {"x": this.x, "y": this.y};
    		const next = {"x": this.x+this.behaviors.Platform.vectorX*runtime.dt, "y": this.y+this.behaviors.Platform.vectorY*runtime.dt};
    		let maxPushoutDistance = 7; // maximum corner correction
    		const maxPushoutSlope = 1000*runtime.dt; // max slope correction
    		const minCeilingAngleTolerance = Math.PI*0.05; // maximum ceiling angle
    
    		// set position to next frame
    		this.setPosition(next.x, next.y);
    
    		let overlap = this.testOverlapSolid();
    		if(overlap) {
    			// store the behaviors current vectorY
    			this.storedY = this.behaviors.Platform.vectorY+this.behaviors.Platform.gravity*runtime.dt;
    			let testRight = false;
    			let testLeft = false;
    
    			// test push right
    			let i = 0;
    			let pushoutAngle = 0;
    			while(overlap && i < maxPushoutDistance) {
    				i++;
    				const bbox = this.getBoundingBox();
    				const ray = this.behaviors.LineOfSight.castRay(bbox.left, bbox.top+16, bbox.left, bbox.top-2); // angle defaults to 0 if no slope hit
    				let rAngle = Util.isWithinAngle(ray.normalAngle, Math.PI*0.5, minCeilingAngleTolerance) ? ray.normalAngle-Math.PI*0.5 : ray.normalAngle;
    				if(Math.cos(rAngle) < 0) rAngle = Math.PI*2;
    				if (ray.didCollide && !Util.isWithinAngle(ray.normalAngle, Math.PI*0.5, minCeilingAngleTolerance)) maxPushoutDistance = maxPushoutSlope;
    				this.x += Math.cos(rAngle);
    				this.y += Math.sin(rAngle);
    				overlap = this.testOverlapSolid();
    				pushoutAngle = rAngle;
    			}
    			testRight = {"success": false, "i": i, "x": this.x, "y": this.y, "angle": pushoutAngle};
    			if(!overlap) testRight.success = true;
    
    			// reset
    			this.setPosition(next.x, next.y);
    			overlap = true;
    			maxPushoutDistance = 7;
    			pushoutAngle = 0;
    
    			// test push left
    			i = 0;
    			while(overlap && i < maxPushoutDistance) {
    				i++;
    				const bbox = this.getBoundingBox();
    				const ray = this.behaviors.LineOfSight.castRay(bbox.right, bbox.top+16, bbox.right, bbox.top-2);
    				let rAngle = ray.didCollide ? ray.normalAngle : Math.PI; // angle defaults to Math.PI if no slope hit
    				rAngle = Util.isWithinAngle(rAngle, Math.PI*0.5, minCeilingAngleTolerance) ? rAngle+Math.PI*0.5 : rAngle;
    				if(Math.cos(rAngle) > 0) rAngle = Math.PI;
    				if (ray.didCollide && !Util.isWithinAngle(ray.normalAngle, Math.PI*0.5, minCeilingAngleTolerance)) maxPushoutDistance = maxPushoutSlope;
    				this.x += Math.cos(rAngle);
    				this.y += Math.sin(rAngle);
    				overlap = this.testOverlapSolid();
    				pushoutAngle = rAngle;
    			}
    			testLeft = {"success": false, "i": i, "x": this.x, "y": this.y, "angle": pushoutAngle};
    			if(!overlap) testLeft.success = true;
    
    			// result
    			if(!testLeft.success && !testRight.success) {
    				this.setPosition(old.x, old.y);
    			} else if(testLeft.success && testRight.success) {
    				// detect inner corner
    				if(Math.sign(Math.cos(testLeft.angle)) != Math.sign(Math.cos(testRight.angle))) {
    					this.didPushout = false;
    				} else {
    					this.didPushout = true;
    				}
    				if(testRight.i >= testLeft.i) {
    					this.setPosition(testLeft.x, testLeft.y);
    				} else {
    					this.setPosition(testRight.x, testRight.y);
    				}
    			} else if (testRight.success && !testLeft.success) {
    				this.setPosition(testRight.x, testRight.y);
    				this.didPushout = true;
    			} else if (!testRight.success && testLeft.success) {
    				this.setPosition(testLeft.x, testLeft.y);
    				this.didPushout = true;
    			}
    
    		} else {
    			// no pushout needed, just revert
    			this.setPosition(old.x, old.y);
    		}
    	}
    }