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 rootbeerking

Hello, I did a quick search for this, and found no answer, so I thought I'd ask:

Basically I would like to make it so that the screen stops scrolling whenever the player reaches the edge of the map.

Thank you for taking the time to read this.

1 decade ago by paularmstrong

I just did this the other day...

What you'll need to do is get the width/height of your map after you call loadLevel (I put that in the bottom of loadLevel, after a parent call)

this._mapWidth = ig.game.backgroundMaps[0].width * ig.game.backgroundMaps[0].tilesize - (ig.system.width);
this._mapHeight = ig.game.backgroundMaps[0].height * ig.game.backgroundMaps[0].tilesize - (ig.system.height);

Then, in your update function, compare if the player's x/y position should affect the screen position:

x = player.pos.x - (ig.system.width / 2);
y = player.pos.y - (ig.system.height / 2);
this.screen.x = (x > 0 && x < this._mapWidth) ? x : this.screen.x;
this.screen.y = (y > 0 && y < this._mapHeight) ? y : this.screen.y;

Hope that helps!

1 decade ago by rootbeerking

Thanks for the reply... But that didn't work. I adapted a different way to do the screen scroll and I tried to apply your code to it and it broke, this is the main part of my main.js file what did I do wrong?

	init: function() {
		// Bind keys
		ig.input.bind( ig.KEY.LEFT_ARROW, 'left' );
		ig.input.bind( ig.KEY.RIGHT_ARROW, 'right' );
		ig.input.bind( ig.KEY.UP_ARROW, 'up' );
		ig.input.bind( ig.KEY.DOWN_ARROW, 'crouch' );
		ig.input.bind( ig.KEY.X, 'jump' );
		ig.input.bind( ig.KEY.C, 'shoot' );
		
		// Load the LevelTest as required above ('game.level.test')
		this.loadLevel( LevelTest );
		this.screenWidth = 480;
		this.screenHeight = 320;
		
		
	},
	
	update: function() {
	// Update all entities and BackgroundMaps
		
		this.parent();
		this.followCamera();
		
		
	// Add your own, additional update code here
},

followCamera: function(xbufL, xbufR, ybufT, ybufB) {
if (!xbufL) xbufL = 32*6;
if (!xbufR) xbufR = xbufL;
if (!ybufB) ybufB = 22*5;
if (!ybufT) ybufT = 32*3;

xcam = this.screen.x; // The current x-coordinate of the camera
ycam = this.screen.y; // The current y-coordinate of the camera

this._mapWidth = ig.game.backgroundMaps[0].width * ig.game.backgroundMaps[0].tilesize - (ig.system.width);
this._mapHeight = ig.game.backgroundMaps[0].height * ig.game.backgroundMaps[0].tilesize - (ig.system.height);

// Here we center the camera on the object we're tracking
var player = this.getEntitiesByType( EntityPlayer )[0];
    if( player ) {
x = player.pos.x.round() + player.size.x / 2;
y = player.pos.y.round() + player.size.y / 2;
    }
    

if ((x - xcam) > (this.screenWidth - xbufR)) this.screen.x = (x < 752) ? xcam + (x - xcam) - (this.screenWidth - xbufR) : this.screen.x;
if((x - xcam) < (xbufL)) this.screen.x = (x > 189 ) ? xcam + (x - xcam) - xbufL : this.screen.x;
if ((y - ycam) > (this.screenHeight - ybufB)) this.screen.y = ycam + (y - ycam) - (this.screenHeight - ybufB);
if ((y - ycam) < (ybufT)) this.screen.y = ycam + (y - ycam) - ybufT;


// Scroll Camera Up
		if( ig.input.state('up') ) {
			 ig.game.screen.y = ycam + (y - ycam) - ybufB;
		}
		
// Scroll Camera Down
		if( ig.input.state('crouch') ) {
			 ig.game.screen.y = ycam + (y - ycam) - ybufT;
		}

},

I'm very new to JavaScript and Impact so forgive me if the solution is very easy...

In addition to my previous question could someone also tell me why my character doesn't move smoothly while the screen is scrolling? Any help would be much appreciated.

1 decade ago by MyShuitings

Here is a snippet from my zelda clone (in progress)... it keeps you centered in the screen until you get to the edge, at which point the background stays static and the entity basically walks off the edge:

scrollScreen: function() {
	  if(this.player.pos.x > (ig.game.collisionMap.width*ig.game.collisionMap.tilesize-ig.system.width/2))
	  {
	    this.screen.x = ig.game.collisionMap.width*ig.game.collisionMap.tilesize-ig.system.width;
	  }
	  else if (this.player.pos.x < ig.system.width/2) 
	  {
	    this.screen.x = 0;
	  }
	  else 
	  {
	    this.screen.x = this.player.pos.x - ig.system.width/2;
	  }
	
	  if(this.player.pos.y > (ig.game.collisionMap.height*ig.game.collisionMap.tilesize-ig.system.height/2))
	  {
	    this.screen.y = ig.game.collisionMap.height*ig.game.collisionMap.tilesize-ig.system.height;
	  }
	  else if (this.player.pos.y < ig.system.height/2) 
	  {
	    this.screen.y = 0;
	  }
	  else 
	  {
	    this.screen.y = this.player.pos.y - ig.system.height/2;
	  }
	},

1 decade ago by Graphikos

I haven't looked too much at your code but choppy movement could due to this "bug".

http://impactjs.com/forums/help/choppy-movement

Dominic does a good job explaining what is going on and how to resolve it but you probably just need to swap the two statements so they are like this:

this.parent();
this.followCamera();

1 decade ago by rootbeerking

Thank you graphikos, I got the choppy movement all figured out. Now all that is left (for now, haha) is the problem of the screen scrolling not stopping when the player reaches the edge of the map.

1 decade ago by MyShuitings

cough cough... read my previous post for the screen scrolling stopping when player reaches edge of map... or more precise when map edge reaches edge of viewport.

1 decade ago by rootbeerking

Aye, I looked at that MyShuitings, but I'm not sure how to adapt that to what I'm currently using.

1 decade ago by paularmstrong

@rootbeerking

in followCamera(), these are synonymous:

ig.game.screen === this.screen

In your `if (player)` section, you're setting x and y incorrectly. Make sure that section reads as so:

if (player) {
    x = player.pos.x - (ig.system.width / 2);
    y = player.pos.y - (ig.system.height / 2);
    this.screen.x = (x > 0 && x < this._mapWidth) ? x : this.screen.x;
    this.screen.y = (y > 0 && y < this._mapHeight) ? y : this.screen.y;
}

After you injected the example that I gave, you have a whole bunch of xcam/ycam set of `if` statements. What is going on there? That just completely moves my screen out of view.

I used your code, and once I fixed the x and y and removed those for `if` statements, everything worked as I expected: EntityPlayer was centered in the view at all times unless it was at the edge of the map.

1 decade ago by rootbeerking

@paularmstrong Well I can't really remove all that stuff like you did, because it messes up the scrolling system I'm using, which is like kinda Super Mario World where the camera doesn't move until the character is in a certain area of the screen. What I'm trying to do is keep that system, but also add to it the feature of stopping the scroll when the player reaches the edge of the map. Sorry for not making that clear...

EDIT2:
Okay so I'm getting somewhere, I figured out how to stop the screen from scrolling... However, I had to do this by hard coding the map values into the code:
if ((x - xcam) > (ig.system.width - xbufR)) this.screen.x = (x < 752) ? xcam + (x - xcam) - (ig.system.width - xbufR) : this.screen.x;
if((x - xcam) < (xbufL)) this.screen.x = (x > 189) ? xcam + (x - xcam) - xbufL : this.screen.x;

Instead of hard coding the end(x < 752) and beginning(x > 189) of my map what would I use instead to grab those values from any map the player might be on?

1 decade ago by Graphikos

@rootbeerking

Be sure to bake your project before putting it on the public forums.
http://impactjs.com/documentation/baking

1 decade ago by rootbeerking

Alright so here is what I'm working with now:

main.js
update: function() {
	// Update all entities and BackgroundMaps
		this.parent();
		
	// Add your own, additional update code here
		this.followCamera();
	},

followCamera: function(xbufL, xbufR, ybufT, ybufB) {
if (!xbufL) xbufL = 32*6;
if (!xbufR) xbufR = xbufL;
if (!ybufB) ybufB = 22*5;
if (!ybufT) ybufT = 32*3;

xcam = this.screen.x; // The current x-coordinate of the camera
ycam = this.screen.y; // The current y-coordinate of the camera

mapx = ig.game.collisionMap.width * ig.game.collisionMap.tilesize; // The current Width of the Map
mapy = ig.game.collisionMap.height * ig.game.collisionMap.tilesize; // The current Height of the Map

// Here we center the camera on the object we're tracking
var player = this.getEntitiesByType( EntityPlayer )[0];
    if( player ) {
	x = player.pos.x.round() + player.size.x / 2;
	y = player.pos.y.round() + player.size.y / 2;
    }
    
//Right and Left Screen Scrolling
if ((x - xcam) > (ig.system.width - xbufR)) this.screen.x = (x < 712) ? xcam + (x - xcam) - (ig.system.width - xbufR) : this.screen.x;
if((x - xcam) < (xbufL)) this.screen.x = (x > 191) ? xcam + (x - xcam) - xbufL : this.screen.x;
//Down and Up Screen Scrolling
if ((y - ycam) > (ig.system.height - ybufB)) this.screen.y = (y < 604) ? ycam + (y - ycam) - (ig.system.height - ybufB) : this.screen.x;
if((y - ycam) < (ybufT)) this.screen.y = (y > 99 ) ? ycam + (y - ycam) - ybufT : this.screen.y;

},

So I'm now getting the correct size of the map, but I still can't use it, I tried "x < mapx/2" and that failed, I tried "xcam < mapx/2" and that sort of worked, but it still scrolled past the map a little bit... So I'm slowly getting there, but still haven't solved the problem. So any help would be appreciated.

1 decade ago by dominic

The max screen.x and y is the collision map size minus the screen size:
var maxX = this.collisionMap.width * this.collisionMap.tilesize - ig.system.width;

Here's the camera class from Biolab Disaster, that uses a "trap" as described in Shaun Inman's excellent video/article.

You can see it in action by starting a game in Biolab Disaster and typing ig.game.camera.debug = true in your browser's console.

Camera = ig.Class.extend({
	trap: {
		pos: { x: 0, y: 0},
		size: { x: 16, y: 16 }
	},
	max: { x: 0, y: 0 },
	offset: {x: 0, y:0},
	pos: {x: 0, y: 0},
	damping: 5,
	lookAhead: { x: 0, y: 0},
	currentLookAhead: { x: 0, y: 0},
	
	debug: false,
	

	init: function( offsetX, offsetY, damping ) {
		this.offset.x = offsetX;
		this.offset.y = offsetY;
		this.damping = damping;
	},
	
	
	set: function( entity ) {
		this.pos.x = entity.pos.x - this.offset.x;
		this.pos.y = entity.pos.y - this.offset.y;
		
		this.trap.pos.x = entity.pos.x - this.trap.size.x / 2;
		this.trap.pos.y = entity.pos.y - this.trap.size.y;
	},
	
	
	follow: function( entity ) {
		this.pos.x = this.move( 'x', entity.pos.x, entity.size.x );
		this.pos.y = this.move( 'y', entity.pos.y, entity.size.y );
		
		ig.game.screen.x = this.pos.x;
		ig.game.screen.y = this.pos.y;
	},
	
	
	move: function( axis, pos, size ) {
		var lookAhead = 0;
		if( pos < this.trap.pos[axis] ) {
			this.trap.pos[axis] = pos;
			this.currentLookAhead[axis] = this.lookAhead[axis];
		}
		else if( pos + size > this.trap.pos[axis] + this.trap.size[axis] ) {
			this.trap.pos[axis] = pos + size - this.trap.size[axis];
			this.currentLookAhead[axis] = -this.lookAhead[axis];
		}
		
		return (
			this.pos[axis] - (
				this.pos[axis] - this.trap.pos[axis] + this.offset[axis]
				+ this.currentLookAhead[axis]
			) * ig.system.tick * this.damping
		).limit( 0, this.max[axis] );
	},
	
	
	draw: function() {
		if( this.debug ) {
			ig.system.context.fillStyle = 'rgba(255,0,255,0.3)';
			ig.system.context.fillRect(
				(this.trap.pos.x - this.pos.x) * ig.system.scale,
				(this.trap.pos.y - this.pos.y) * ig.system.scale,
				this.trap.size.x * ig.system.scale,
				this.trap.size.y * ig.system.scale
			);
		}
	}
});

This class supports damping for the camera movement and a "lookahead" - which moves the screen a bit in the direction you're facing, so you can see where you're going. In Biolab Disaster, the lookahead is only used in the mobile version.

Here's the code needed in your game class to make it work, as from Biolab Disaster:

init: function() {	
	this.camera = new Camera( ig.system.width/4, ig.system.height/3, 5 );
	this.camera.trap.size.x = ig.system.width/10;
	this.camera.trap.size.y = ig.system.height/3;
	this.camera.lookAhead.x = ig.ua.mobile ? ig.system.width/6 : 0;
},

loadLevel: function( level ) {		
	this.parent( level );

	this.player = this.getEntitiesByType( EntityPlayer )[0];
	
	// Set camera max and reposition trap
	this.camera.max.x = this.collisionMap.width * this.collisionMap.tilesize - ig.system.width;
	this.camera.max.y = this.collisionMap.height * this.collisionMap.tilesize - ig.system.height;
	
	this.camera.set( this.player );
},

update: function() {
	this.camera.follow( this.player );
	this.parent();
}

If you want to see the camera trap, call this.camera.draw() in your game's draw() method and set this.camera.debug to true.

I apologize for this mess. I've been meaning to clean this up and release it with Impact for a while now, but... oh well.

1 decade ago by rootbeerking

Thank you very much for the reply Dominic, I really appreciate it! However, I got an error involving "this.pos.x = this.move( 'x', entity.pos.x, entity.size.x ); " saying "entity is undefined" when using that code you gave me. What did I do wrong? I made a camera.js in plugins using

ig.module( 
    'plugins.camera' 
)
.requires(
    'impact.impact'
)
.defines(function(){

//Camera Class Code Here

});

Then I did the set up code like you told me to do in my main.js file, then I tested my game and that error that I mentioned above pops up over and over while the game tries to load.

1 decade ago by dominic

Make sure you really hand over an entity to camera.follow(). As in the example above, this.player is set when loading a level.
this.player = this.getEntitiesByType( EntityPlayer )[0];

and then passed to camera.follow() in the update() method:
this.camera.follow( this.player );

1 decade ago by rootbeerking


EDIT: YAY! I figured it out! I was missing "this.loadLevel( LevelTest );" at the end of the init function! Thank you everyone for your help, and especially thank you Dominic for sharing your great camera code! Now I just need to tweak it a little bit and I'll be all set for that bit... Now I gotta figure out how to get tighter feeling moving controls.

Oh, but one last question in regards to the camera: If I wanted to manually take control of it for like a cut scene, how would I do that exactly?

UPDATE: Hmm there seems to be the issue with the choppy movement again now... It's not terribly noticeable but is there anyway at all to get the character to not twitch while the screen is scrolling?

1 decade ago by axphin

Did you figure out the choppy movement?

1 decade ago by rootbeerking

Not really. I got it down to having the choppy movement only happen when the screen first starts to scroll, but I haven't completely eliminated it.

1 decade ago by sleenee

@dominic
this is a really nice working solution for a camera that follows an entity. Thank you for this.
However I have a question. Which variable do you use to correct the mouse-coordinates? Since the ig.input.mouse.x (and ig.input.mouse.y) will not correspond to the actual x and y map values when the map was scrolled, you can not click on an object accuratey. Could you tell me how to correct for this please?

Thanks in advance

1 decade ago by dominic

Just add the screen coordinates to the mouse coordinates to get the position in the game world:
var realX = ig.input.mouse.x + ig.game.screen.x;
var realY = ig.input.mouse.y + ig.game.screen.y;

1 decade ago by sleenee

That seems to work flawlessly, thank you very much!

1 decade ago by 80bit

For any noobs like me, be sure to REMOVE any basic screen-following functionality you may have already had in there. ;) ;) ;) ;) (SMH)

Works awesome, thank you so much for this code!

1 decade ago by Sven

Thanks a lot for this great class, I added a acceleration check to the move method. In my case I have a player the bounces off the wall, kinda annoying when you use look ahead and you bounce back while you still want to go the other way. Anyways here is the code, thanks again.

follow: function( entity ) {
    this.pos.x = this.move( 'x', entity.pos.x, entity.size.x, entity.accel.x );
    this.pos.y = this.move( 'y', entity.pos.y, entity.size.y, entity.accel.y );
    
    ig.game.screen.x = this.pos.x;
    ig.game.screen.y = this.pos.y;
},

move: function( axis, pos, size, accel ) {
    var lookAhead = 0;
    if( pos < this.trap.pos[axis] ) {
        this.trap.pos[axis] = pos;
        if (accel < 0 || accel==0) {
            this.currentLookAhead[axis] = this.lookAhead[axis];
        }
    }
    else if( pos + size > this.trap.pos[axis] + this.trap.size[axis] ) {
        this.trap.pos[axis] = pos + size - this.trap.size[axis];
        if (accel > 0 || accel==0) {
            this.currentLookAhead[axis] = -this.lookAhead[axis];
        }
    }

    return (
        this.pos[axis] - (
            this.pos[axis] - this.trap.pos[axis] + this.offset[axis]
            + this.currentLookAhead[axis]
        ) * ig.system.tick * this.damping
    ).limit( 0, this.max[axis] );
}

1 decade ago by SlouchCouch

Oh wow thanks for posting the camera class code. I wish i would have found this sooner, I just made my own but isn't nearly as classy. Also I made it an entity so it wastes a lot of time doing entity update stuff when I could have just explicitly told it what to do after making it a class.

1 decade ago by alexandre

And another yeah! for Dominic's camera code. This should be part of the next release, IMO. Tres useful.

1 decade ago by Donzo

Thanks!
It works awesomely.

+1

1 decade ago by benkz

Hello this looks like an really cool camera plugin thanks a lot for sharing, unfortunately it doesn't work in my little project. i followed the instructions above.
but i keep getting the message in firebug that the

"camera is not defined...
this.camera = new Camera( ig.system.width/4, ig.system.height/3, 5 );" main.js .46

do I have to declarate the camera first above the init() in
MyGame = ig.Game.extend({...?

edit: ok i'm confused :) right now. where do i need to place the code
in my main.js or into the game.js within impact?

firebug now says
uncaught exception: Unresolved (circular?) dependencies. Most likely there's a name/path mismatch for one of the listed modules: game.main (requires: plugins.camera.camera)

Update: oh boy finally it works! classic noob mistake i guess. i placed the camera,js
within the path plugins/camera/camera.js and tried to call it from main with

.requires{... 'plugins.camera.camera'...}

which didnt work, so i get rid of the folder an just put the file into the plugins folder and .requires{... 'plugins.camera'...}

tadaa! :) thanks again for sharing this peace of awesome code Dominic!

1 decade ago by bitmapshades

I've been following along with this example but I get an error with Dominic's camera class when I make it a separate plugin but it's throwing an error within the loadLevel function. How can I fix this?

TypeError: this.camera is undefined
[Break On This Error]
...camera.max.x = this.collisionMap.width * this.collisionMap.tilesize - ig.system....


Main.js
loadLevel: function(data) {
		this.parent(data);
		this.levelTimer.reset();
		this.player = this.getEntitiesByType( EntityPlayer )[0];

		// Set camera max and reposition trap
		this.camera.max.x = this.collisionMap.width * this.collisionMap.tilesize - ig.system.width;
		this.camera.max.y = this.collisionMap.height * this.collisionMap.tilesize - ig.system.height;
	
		this.camera.set( this.player );
	},

Camera.js
ig.module(
	  'game.camera'
)
.requires(
	  'impact.impact'
)
.defines(function(){
Camera = ig.Class.extend({
//camera code
});
});

1 decade ago by jerev

@bitmapshades

Did you actually make a camera object?

init: function() {
    this.camera = new Camera( ... ); // new Camera(offsetX, offsetY, damping)
}

And did you make it before loading a level?

Note, if you use the director plugin, you should create your director after you made the camera, because new ig.Director() will call a level load.

init: function() {
    this.camera = new Camera( ... );
    ...
    this.director = new ig.Director( ... ); // Will call a level load.
}

1 decade ago by PaulB

I've implemented this camera and it's working except when my player dies, which is weird because the kill() method doesn't reference the camera at all. As soon as the player dies, I get this error:

Uncaught TypeError: Cannot read property 'pos' of undefined - line 41, camera.js

which corresponds with this line (beginning this.pos.x) in camera.js

	follow: function( entity ) {
	    this.pos.x = this.move( 'x', entity.pos.x, entity.size.x, entity.accel.x ); 
	    this.pos.y = this.move( 'y', entity.pos.y, entity.size.y, entity.accel.y );
	    ig.game.screen.x = this.pos.x;
	    ig.game.screen.y = this.pos.y;
	},

That's getting called by this line in my update method:
    	this.player = this.getEntitiesByType( EntityPlayer )[0];
    	this.camera.follow( this.player );

I can't see what's wrong with it, but every time the player dies, so does the game! :-/
Page 1 of 2
« first « previous next › last »