Impact

This forum is read only and just serves as an archive. If you have any questions, please post them on github.com/phoboslab/impact

1 decade ago by KirbySaysHi

Hopefully a diagram can help explain:

/><br />
<br />
I have a fast-moving projectile (the dark rectangle in the diagram) that upon colliding with a collidable tile, will spawn a new entity where it hit. When using sloped tiles, it appears that ig.game.collisionMap.trace does not return the initial point of contact (point A in the diagram), but instead returns the corrected point after accounting for entity velocity and angle of tile slope (point B in the diagram). This is resulting in my projectiles sliding across the sloped tile! This is probably the behavior most people want, but I just need the actual point of impact, uncorrected.<br />
<br />
Again, the res object does not contain anything about which tile was initially hit, only which tile the entity would collide with after correcting for embedding.<br />
<br />
Is is possible to get uncorrected collision information, without copy/rewriting/modifying ig.game.collisionMap.trace?			</div>
		</div>
			<div class=

1 decade ago by dominic

That point is actually never calculated in Impact's collision routines. I noticed this too when I had a similar problem in my game recently - It's really unfortunate. I'll add this in Impact 2. Somehow.

Here's a workaround. However, this only works with non-sloped tiles, where the bullet is hitting the wall at an angle. You essentially have the same problem, only with straight traveling bullets and angled walls - so maybe this code helps a bit.

handleMovementTrace: function( res ) {
	var nx = res.pos.x,
		ny = res.pos.y;
	
	if( res.collision.x || res.collision.y ) {		
		// The bullet will "slide" on the wall a bit, because it's so
		// fast, but we don't want that. Figure out the fraction of the 
		// velocity that the bullet did travel on either axis and only 
		// honor the smaller one
		var nvx = nx - this.pos.x;
		var nvy = ny - this.pos.y;
		
		if( this.vel.x && this.vel.y ) { // Make sure we dont divide by 0
			var fvx = nvx / this.vel.x,
				fvy = nvy / this.vel.y;
				
			// Fraction of traveled distance on one axis bigger than on 
			// the other?
			// Apply the smaller fraction to the axis that had the bigger 
			// (wrong) fraction
			if( fvx > fvy ) {
				nx = this.pos.x + this.vel.x * fvy;
			}
			else {
				ny = this.pos.y + this.vel.y * fvx;
			}
			this.vel.x = this.vel.y = 0;
		}
	}
	
	this.pos.x = nx;
	this.pos.y = ny;
},

1 decade ago by KirbySaysHi

dominic,

I think I found a solution to the problem. It took me a while to comprehend how the _traceStep and _checkTileDef methods worked (as well as how slopes work internally), but I created a plugin that adds to every collision:

 	res.collision.snx: surface normal x
 	res.collision.sny: surface normal y
 	res.collision.initial = { x, y }: the point at which the projected velocity line collides with a tile

The code is in this gist: https://gist.github.com/3054439, which contains the plugin, as well as a math helper class I used to do line-line intersection calculations.

It injects wrappers around _checkTileDef and _traceStep, which use the information given to those methods + what they return to do a line-line intersection test on the original velocity vector and the line perpendicular to the collision surface normal.

I also "normalized" (not in the vector sense) the res object, so that it always returns useful information about the collision, such as the surface normal (which == the slope on a slope collision, but is now present for non-slope collisions too), and the initial point of collision regardless of the collision type.

I feel that the "real" way to do this, is:

1) Make a raycasting class that can cast a ray over the map. it would return useful information about the collided tile, such as surface normal, x/y px position, tileX, tileY.
2) Make the collisionmap.trace method use the raycasting class, and also determine where a "valid" position would be (like now), while also returning the same types of results as the current res object, but consistent regardless if it's a sloped collision or not.

The raycasting class would be very useful for "instabullets", lasers, line of sight, etc.

There has to be a better way to get this information than my solve... and I'm not sure it will work in all cases. I tried it with slopes and walls, as well as with projectiles coming in at an angle.

EDIT: I realized that one thing would make this much easier: if in _traceStep, it attached to res the tile that was hit. Then, even if _checkTileDef projects out at an angle, the original tile is still available. Then, this entire process could be handled in handleMovementTrace.

1 decade ago by paulh

Hey kirby

This is brilliant, but how do you use it?

Is like an entity to rebound at the correct angle from a sloped collision tile?

How do you get the collision location form your plugin and then work out the angle of bounce from it

1 decade ago by paulh

Sorry for my ignorance, but whats the difference between your solution and this:

http://impactjs.com/forums/help/calculating-the-angle-of-reflection-with-sloped-collision-map-tiles


  calculateVelocity: function(angle){
            this.initialVel.y = Math.sin(angle) * this.desiredVel;
            this.initialVel.x =  Math.cos(angle) * this.desiredVel;
            
            this.vel.x = this.initialVel.x;
            this.maxVel.x = this.initialVel.x;

            this.vel.y = this.initialVel.y;
            this.maxVel.y = this.initialVel.y;
            this.angle = angle;
        },
        
        getAngle: function(){
            return Math.atan(this.vel.y/this.vel.x);
        },
        
        handleMovementTrace: function(res){
                        
            this.parent(res);

            
            if (res.collision.x || res.collision.y || res.collision.slope){
                var pi = Math.PI;
             
                this.bounceCounter++;
                console.log(this.bounceCounter);
                if (this.bounceCounter > this.bounceLimit)
                {
                    this.kill();
                }
                
                console.log("Before Angle: " + this.r2d(this.angle));
                
                this.previousAngle = this.angle;
                
                var ny;
                var nx;
                
                var tx;
                var ty;
                
                var iAngle;
                var x;
                if (res.collision.x)
                {
                    console.log("*** HIT X ***");
                    nx = 0;
                    ny = 1;

                    tx = 1;
                    ty = 0;
                    var x = iAngle = pi - (this.angle);
                }
                
                if (res.collision.y)
                {
                    console.log("*** HIT Y ***");
                    nx = 1;
                    ny = 0;

                    tx = 0;
                    ty = 1;
                    var x = iAngle = -(this.angle);
                }
                
                if (res.collision.slope)
                {
                    console.log("*** HIT SLOPE ***");

                    var slope = res.collision.slope;
                    
                    ny = slope.ny;
                    nx = slope.nx;
                    
                    tx = slope.x;
                    ty = slope.y;
                    
                    iAngle = this.angle * -1;
                    
                    var nAngle = Math.atan2(ny, nx);
                    var tAngle = Math.atan2(ty, tx);
                    
                    var d = pi - iAngle;
                    var d2;
                    
                    if ((nAngle >= 0 && tAngle >= 0) || (nAngle <= 0 && tAngle <= 0))
                    {               
                        d2 = pi - (d + (pi/2));
                    }
                    else
                    {
                        d2 = -(pi + d) + (pi/2);
                    }

                    
                    x = (d2);
                }
                     
                    this.calculateVelocity(x);
            }
        },


Or how would you use the two sets of code to get a perfect bounce of slope tiles?

UPDATED: see below

1 decade ago by Robodude

I forgot to update the code you are referencing in this post, paulh. I edited the original though :)

1 decade ago by paulh

updated, robo, does this do the same thing, even with a small entity hitting middle of a slope?

1 decade ago by Robodude

It seems to work fine no matter where it hits the slope.

/><br />
<img src=

1 decade ago by KirbySaysHi

Hey guys, sorry for the delay. Is there a way to get email notifications for this forum?

paulh, I believe the difference is slight, but important. How fast was that entity moving in your test above? If the yellow is spawned entities, the result will be very different at high speeds.

My issue was that given a bounciness of 0, when an entity impacted a slope, it still slid along the slope instead of just stopping, if you used res.pos. The sliding was not something that I could opt out of, because there was no way to get absolute pixel coordinates from the properties in res. It has the tile slope, but that's worthless if you don't know where the tile is on the map!

So from within handleMovementTrace, there was no way for me to get the point of impact without manually tracing the tile grid via the entity's velocity (and by manual I mean new code). The collision map tracing methods automatically attempt to find a valid position, rather than letting you deal with it. In almost every case this is exactly what people want: you want the entity to bounce, and not have to actually worry about it.

It's my understanding that if you set an entity's "bounciness" to a non-zero value, then everything should just work: the entity will "bounce" at the correct angle. What will actually happen though, is that _checkTileDef will solve for the position, given the angle of impact and velocity.

I'm not sure what the code by @Robodude that @paulh posted is trying to do, as it seems like the result of that code is what impact would do anyway (correct me if I'm wrong). If the angle of impact is desired, then reflecting the velocity vector along the slope normal (nx, ny) would produce that.

So my code hopefully does a few things:

1) Normalizes the res object, so that you always receive a collision surface normal (res.collision.snx/sny), even when colliding with a non-sloped tile. This simplifies calculations, since you no long have `if collision.x || collision.y` as well as slope stuff.
2) Always provides the absolute point of impact (res.collision.initial), even with a very fast moving object. This allows me to know which tile was hit, and where that tile is on the map.

It does not affect how impact solves for collisions, only reports more data ON the collision, which gives you a chance to do something about it. So, as to "how to use it", it just adds some properties to the res object, and if you want to access them you can. For me, this enabled checking for valid portal placement (e.g. a portal must have two contiguous tiles), as well as make sure that the portal projectiles didn't slide, skip, or bounce... it's really one step away from a raytrace. :)

Does that all make sense?
Page 1 of 1
« first « previous next › last »