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

9 years ago by Sledge

What I am trying to implement is a camera that follows the player with some lag. My method is to compute the distance vector comparing the player position and the screen position, then I move the screen along that line a step size every frame.

However, when I run this code, the screen flies off to the upper left indefinitely. Can anyone see where I may have made a mistake?

Below is the code from the update method in my MyGame entity in main.js.




update: function() {
		

		//  screen tracks the player
		var player = this.getEntitiesByType( EntityPlayer )[0];
		if( player ) {	
			var speed = 5;
			var deltat = 1.0;
			var vx = (player.pos.x - this.screen.x);
			var vy = (player.pos.y - this.screen.y);
			var mag = Math.sqrt(Math.pow(vx,2) + Math.pow(vy,2));
			
			var ux = vx/(mag);
			var uy = vy/(mag);

			this.screen.x = this.screen.x + ux*deltat*speed - ig.system.width/2;
			this.screen.y = this.screen.y + uy*deltat*speed - ig.system.height/2;

		}


		// Update all entities and backgroundMaps
		if( !this.showStats ) {
			this.parent();
		} else {
			if( ig.input.state( 'contine' )){
				this.showStats = false;
				this.levelExit.nextLevel();
				this.parent();
			}
		}
		
		

	},
	

9 years ago by dungeonmaster

It flies off to the upper left cos on each update you subtract the system width and system height from the screen position. To calculate the center of the screen position, you should do that before calculating the velocity vector.

also requesting the player one each update with getEntitesByType is a bit overkill. Better assign smt.like this in your games init, after the level is loaded:
ig.global.myPlayer = this.getEntitiesByType( EntityPlayer )[0];
then use ig.global.myPlayer whenever you want to refer to the player.

It should be smt. like this:
var dx = (player.pos.x + player.size.x/2) - (ig.game.screen.x+ig.system.width/2)
var dy = "similar as above"
var mag = Math.sqrt(dx*dx+dy*dy);
var ux = dx/mag
var uy = dy/mag
var speed = 5 //speed of the camera, in pixels per second
ig.game.screen.x += ux*speed*ig.system.tick; // mult. with the tick ensures a steady speed independent of the framerate

(not sure of += can be -= also, too lazy to think right now)

9 years ago by Sledge

Thanks for the response dungeonmaster. I also caught that error but my problem persists. My new code looks like this:


// v2: screen tracks the player
		var player = ig.global.myPlayer;
		if( player ) {	
			var speed = 5.0;
			var vx = player.pos.x - (this.screen.x - ig.system.width/2);
			var vy = player.pos.y - (this.screen.y - ig.system.height/2);
			var mag = Math.sqrt(Math.pow(vx,2) + Math.pow(vy,2));
			
			var ux = vx/(mag);
			var uy = vy/(mag);
		
			this.screen.x = this.screen.x + ux*speed*ig.system.tick;
			this.screen.y = this.screen.y + uy*speed*ig.system.tick;
			
			if( player.accel.x > 0 && this.instructText)
				this.instructText = null;

		}



The screen is now tracking towards the bottom right hand side of the screen at a constant rate. It is as if the player coordinates are not being correctly passed.

Will update if I find a solution...

9 years ago by Joncom

Quote from Sledge
What I am trying to implement is a camera that follows the player with some lag.
This would more or less accomplish that...

/* in main.js update function */
var delay = 200;
ig.game.screen.x += ig.game.player.pos.x / delay;
ig.game.screen.y += ig.game.player.pos.y / delay;

9 years ago by Sledge

Hi Joncom, you're right, but I should have said that the real goal here was to avoid the sudden jump that happens when my character teleports. So I should have asked: how I do I smooth the camera motion when the player position changed suddenly?

Here is the solution I settled on. It turns out that creating a unit vector was dumb. It forces the screen to track even when the distance between the player and screen center is arbitrarily small (presumably some sort of rounding error).

Also my screen offset was the opposite of what I needed, simple flip of the minus sign fixed that.

Here is the code for anyone that wants to copy this feature:

		var player = ig.global.myPlayer;
		if( player ) {	
			var speed = 12.0;
			var vx = player.pos.x - (this.screen.x + ig.system.width/2); 
			var vy = player.pos.y - (this.screen.y + ig.system.height/2); 
		
			this.screen.x = this.screen.x + vx*speed*ig.system.tick;
			this.screen.y = this.screen.y + vy*speed*ig.system.tick;
			
			if( player.accel.x > 0 && this.instructText)
				this.instructText = null;

		}

9 years ago by drhayes

I'm not super happy with how I've got my camera working in my game, but I've got a couple of tricks I can share.

I don't make a constant time shift towards the player, I halve the distance between where the camera is now and where the player is and move the camera there. Put a little damping on it when the values are small and this works pretty well even with unexpected speed-ups.

You could probably add code that says, "If the player is more than 300 pixels away (x or y) then just move the camera there directly."

My camera lives in its own class and it tracks the player like this:

      // Set up the 'track' property.
      Object.defineProperty(this, 'track', {
        get: function() {
          // Find the player.
          if (!track) {
            track = ig.game.getEntityByName('player');
            if (!track) {
              return;
            }
            this.track = track
          }
          return track;
        }.bind(this),
        set: function(newTrack) {
          track = newTrack;
          if (!track) {
            return;
          }
          track.once('kill', function() {
            track = null;
          });
          this.offset.x = Math.round(track.size.x / 2);
          this.offset.y = Math.round(track.size.y / 2);
          if (!this.pos) {
            this.pos = {
              x: track.pos.x + this.offset.x,
              y: track.pos.y + this.offset.y
            };
          }
        }.bind(this)
      });

It tracks the player by default.

There's code in there that says, "Hey, if this is the first time I've tracked the entity move directly there."

I'm using a sweet observable plugin for entities so they can emit events. I subscribe to the 'kill' event to stop tracking when the tracked entity dies.

My camera limits by the size of the map, too:

      ig.game.screen.x = ig.game.screen.x.limit(this.min.x, this.max.x);
      ig.game.screen.y = ig.game.screen.y.limit(this.min.y, this.max.y);

And I figure out the size of the map for limiting by iterating over all the background maps on loadLevel.

On my todo list:

* Camera trap.
* Different behaviors for different parts of the map, e.g. don't show the secret area unless the player has discovered it, etc.

9 years ago by Sledge

@drhayes - thanks for the helpful tips. The camera trap is something that I definitely want to add at some point. For the observable plug in - are there any good lessons or demos out there that show how to use this feature?

9 years ago by drhayes

Not that I know of. My most common use case is my main game object is firing events that different entities listen to in the game... so the game does this.fireEvent('levelLoaded'); and I'll have an entity that does:

ig.game.on('levelLoaded', function() {
   this.kill();
}.bind(this));

Is that what you're looking for?
Page 1 of 1
« first « previous next › last »