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

8 years ago by BennyT

Hi all, I am working on a sports game and I have been trying to debug this issue for the last two days and not making much progress and would appreciate some help.

I have moved my player control functions into a separate entity's update method, and there is a variable where I keep a reference to the entity I wish to control.

The issue though is this conditional statement in the update method:

    if (ig.input.pressed('switch') ) {
	console.log('pressed switch');
	ig.game.getEntitiesByType('EntityTeam')[0].giveOtherPlayerControl();
}

that goes and finds the player's team and looks for the new entity to give control to.

The method that gets called looks like this (I have simplified the team to two entities for debugging purposes):

	giveOtherPlayerControl: function() {
		var controlledPlayer = this.findPlayerWithControl();
		if(controlledPlayer.playerNumber >= 1 ) {
			console.log("this player number: " + this.playersInTeam[1].playerNumber);	
			//controlledPlayer.controlObject = null;
			//controlledPlayer.controller = "computer";
			//this.playersInTeam[0].controller = "player";
			//this.playersInTeam[0].controlObject = new EntityPcontrols(0,0, {player: this.playersInTeam[0]});				
		} else {
			console.log("this player number: " + this.playersInTeam[0].playerNumber);
			controlledPlayer.controlObject = null;
			controlledPlayer.controller = "computer";
			this.playersInTeam[1].controller = "player";
			this.playersInTeam[1].controlObject = new EntityPcontrols(0,0, {player: this.playersInTeam[1]});
		}
	},

The reason why some of the lines are commented out is because when I run the Chrome debugger, after it has done one pass, even though I have only pressed the key once, it goes back again, so it always ends up with the same entity with the control.

When the lines remained commented out, it still runs twice, but the correct entity receives control.

What am I doing wrong????

Thanks for your help in advance!

8 years ago by Joncom

even though I have only pressed the key once, it goes back again, so it always ends up with the same entity with the control
So you're saying that pressing the button once, results in console.log('pressed switch'); being called twice?

If so, is there more than one entity that contains this code?

8 years ago by BennyT

Hi Joncom,

Thanks for the reply!

Yes your understanding is correct, the log statement prints twice.

Since that post I have made some more changes.

Here is the init function for the team (creates a team and sets the first entity to be player controlled):


		// this is how the entity starts based on these parameters
		init: function( x, y, settings ) {
			this.parent( x, y, settings );
			this.name = 'home';

			// create an array to hold player entities in team
			if( !ig.global.wm ) { // Not in WM?
				for(var x = 0; x < this.playerCount; x++) {
					var tempPlayer = ig.game.spawnEntity( 
						"EntityTeammate", 
						(this.pos.x + (x+1)*42), 
						(this.pos.y),
						{
							playerNumber: x, // the number of the player will be the position in the array
							teamName: this.teamName,
							controller: 'computer'
						});
					this.playersInTeam.push(tempPlayer);
				}
				this.playersInTeam[0].controller = "player";
				ig.game.playerController = new EntityPcontrols(0,0, {player: this.playersInTeam[0]});

			}
		},

I created a method to pick any teammate that doesn't share the same id (once I have solved this I will put the find player logic back, but for now it can be any:



findAvailablePlayer: function(currentPlayer) {
			for( var i=0, l=this.playersInTeam.length; i<l; i++ ) {
				if(this.playersInTeam[i].id != currentPlayer.id ) {
					console.log("I can switch to entity id: " + this.playersInTeam[i].id );
					return this.playersInTeam[i];
				} 
			}
			return "oops";		
		},

Rewrote the switch call in the pressed function:


if (ig.input.pressed('switch') ) {
				console.log('pressed switch');
				this.switchPlayer();
			},

And here is the actual switch logic for now


switchPlayer: function() {	
			console.log("the player entity that has control: " + this.entity2control.id);
		
			this.entity2control.controller = "computer";
			this.entity2control = ig.game.getEntitiesByType('EntityTeam')[0].findAvailablePlayer(this.entity2control);
			console.log("this should be the next entity: " + this.entity2control.id);
			this.entity2control.controller = "player";
		},

Even with these changes, the log statement is still getting called twice.

the log statements print out the following order:


pressed switch
pcontrols.js:45 the player entity that has control: 7
team.js:57 I can switch to entity id: 8
pcontrols.js:49 this should be the next entity: 8
pcontrols.js:99 pressed switch
pcontrols.js:45 the player entity that has control: 8
team.js:57 I can switch to entity id: 7
pcontrols.js:49 this should be the next entity: 7

that is after just one press of the nominated key on the keyboard (not mouse button or gamepad for the time being)

It really has got me stumped.

I am only storing a reference to the player entity in the Pcontrols entity (entity2control variable).

Once again, thanks in advance!

8 years ago by Joncom

So your ig.input.pressed('switch') check is in the update function of "pcontrols.js". Is that correct?

And you only check ig.input.pressed('switch') in that one spot, and no where else in your entire codebase, right?

And you only update "pcontrols.js" once per frame, right?

Assuming yes to all of the above, it's very strange that you're seeing the pressed event twice in a single frame...

Would you mind posting the entire "main.js" and "pcontrols.js" files?

8 years ago by BennyT

Correct - the pressed('switch') is inside the pcontrols entity and is only called from there.

I think you might be onto something for the calling of the function once per frame.

Hopefully the code snippets will help.

Here is the main.js:

ig.module( 
	'game.main' 
)
.requires(
	'impact.game',
	'impact.font',
	'impact.debug.debug',
	'game.entities.player',
	'game.entities.enemy',
	'game.entities.trigger',
	'game.entities.pcontrols',
	'game.entities.team',
	'game.levels.two',
	'plugins.astar-for-entities'
)
.defines(function(){

MyGame = ig.Game.extend({
	myfont: new ig.Font( 'media/font.png' ),
	player_score: 0,
	computer_score: 0,
	timer: new ig.Timer(100),
	kickoff: false,
	playerController: null,


	init: function() {
		// Initialize your game here; bind keys etc.
		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, 'down');
		ig.input.bind(ig.KEY.Z, 'passleft');
		ig.input.bind(ig.KEY.X, 'passright');

		// using just to test the switch player functionality
		ig.input.bind(ig.KEY.C, 'switch'); 
		
	    this.loadLevel(LevelTwo);
	},

    loadLevel:function (data)
    {
    	ig.game.timer.reset();
        this.currentLevel = data;
        this.parent(data);
    },

	update: function() {
		// Update all entities and backgroundMaps
		if(this.timer.delta() >= 0) {
			this.timer.reset();
			this.timer.pause();
			ig.game.togglePause();
		}
		if(this.kickoff === true) {
			console.log("reset positions");
		}
		this.parent();
	},
	
	draw: function() {
		// Draw all entities and backgroundMaps
		this.parent();
		// Add your own drawing code here
		this.myfont.draw("Time left: " + Math.round(this.timer.delta(),0) + " PLAYER: " + this.player_score + " COMPUTER: " + this.computer_score, 10, 10);
		
		if(this.referee.tackleCount == 5) {
			ig.game.myfont.draw("LAST TACKLE", 10, 700);
		}
		if(this.referee.tackleCount == 6) {
			ig.game.myfont.draw("HAND OVER", 10, 700);
		}
	},

	afterTry: function() {
		console.log("restart after the try");
	}
});

And the entire pcontrols.js:

ig.module(
	'game.entities.pcontrols'
) 
.requires(
	'impact.entity'
)
.defines(function(){
	EntityPcontrols = ig.Entity.extend({
		name: "pcontrols",
		entity2control: null,
		_wmIgnore: true,

		// this is how the entity starts based on these parameters
		init: function( x, y, settings ) {
			this.parent(settings);
			if( !ig.global.wm ) { // Not in WM?
				this.entity2control = settings.player;
			}
		},

		switchPlayer: function() {	
			console.log("the player entity that has control: " + this.entity2control.id);
		
			ig.game.playerController.entity2control.controller = "computer";
			ig.game.playerController.entity2control = ig.game.getEntitiesByType('EntityTeam')[0].findAvailablePlayer(this.entity2control);
			console.log("this should be the next entity: " + this.entity2control.id);
			ig.game.playerController.entity2control.controller = "player";
		},

		// keypresses and movement code in the object
		update: function() {
			// move! 
			this.parent();

			// temp variable to track the ball
			var ball = ig.game.getEntityByName("ball");
			
			// general player movement code
			if( ig.input.state('up') ) {
			    this.entity2control.vel.y = -this.entity2control.speed;
			}
			else if( ig.input.state('down') ) {
			    this.entity2control.vel.y = this.entity2control.speed;
			}
			else if( ig.input.state('left') ) {
			    this.entity2control.vel.x = -this.entity2control.speed;
			}
			else if( ig.input.state('right') ) {
			    this.entity2control.vel.x = this.entity2control.speed;  
			}
			else // if no button is pressed, they should stop moving
			{
			    this.entity2control.vel.x = 0;
			    this.entity2control.vel.y = 0;
			}

			// if the player wants to pass right
			if(ig.input.pressed('passright') ) {
				// and the player also has possession of the ball
				if(this.entity2control.possession === true) {
					this.entity2control.possession = false;
					ball.owner = null;
					ball.update("right");
				}
			} 

			if (ig.input.pressed('passleft') ) {
				if(this.entity2control.possession === true) {
					this.entity2control.possession = false;
					ball.owner = null;
					ball.update("left");
				}
			}

			if (ig.input.pressed('switch') ) {
				console.log('pressed switch');
				this.switchPlayer();
			}

			// make sure the animations for the sprite match the current state
			if( this.vel.y < 0 ) {
				this.currentAnim = this.anims.run;
			}else if( this.vel.y > 0 ) {
				this.currentAnim = this.anims.run;
			}else if( this.vel.x > 0 ) {
				this.currentAnim = this.anims.right;
			}else if( this.vel.x < 0 ) {
				this.currentAnim = this.anims.left;
			}else{
				this.currentAnim = this.anims.idle;
			}

		}
	});
});

And here is where the teammate entity that is stored in the playersInTeam array, makes a call to the player controls update to move the player controlled entity:

		update: function() {

			// something is going wrong here, need to debug.
			if(this.controller != "computer") {
				ig.game.playerController.update();
			} else  {
				this.flock();
			}

			// move! 
			this.parent();
		},

I think it has something to do, based on your reply and me thinking it through today that I am actually calling it twice maybe?

If so, what is the best way for me to access the play controls entity from the teammate entity.

I haven't had a chance to properly debug today, but I think we could be close to solving this.

Once again, really appreciate the help!

8 years ago by Joncom

I suspect this is your problem:

ig.game.playerController.update();

Entities are already updated every frame automatically.

So you're causing the playerController entity to be updated yet another time on any frame where:

this.controller != "computer"

8 years ago by stahlmanDesign

@Joncom that looks like the problem. I can't think why you would call update() like that because the Entity itself has an update method that is called each cycle.

8 years ago by BennyT

Hi guys,

Thanks for your help again, but now I am a bit stuck

That if statement is instead the teammate entity, if I remove that if statement only and leave everything else the same, the entity with the controller = player does not move.

I guess the question I have now is where should the logic to check which player should be moved, and how can I call the functions that register the key presses to then move the player.

Should I even have the playerController entity in the global game space, or should I store it somewhere else.

Sorry for dragging this out for so long, hopefully we can help others in this situation!

8 years ago by Joncom

where should the logic to check which player should be moved, and how can I call the functions that register the key presses to then move the player.
Are you trying to have multiple entities be controllable, but only one at a time? And you're using playerController to receive input and move the controlled entity? If so, I don't see why that wouldn't work...

1) You have a property playerController.entity2control.

2) Every time you "switch entities", the value of entity2control points to a different entity.

3) Every frame, playerController reads ig.input and moves the currently controlled entity if necessary.

I'm not sure I see the problem?

Edit:

Another approach you could take is: give each entity a .active property (or something similar), and add a check in the update function:

update: function() {
    if(this.active) {
        if(ig.input.state('left') {
            this.vel.x = -10;
        }
        if(ig.input.state('right') {
        ...
    }
    this.parent();
}

That way, you just make sure whatever entity you want to control is the one that has .active === true.

8 years ago by BennyT

Hi Joncom,

Yes I am definitely wanting to have a collection of entities have the possibility of being controllable, but only one at a time.

The steps you have outlined are correct, and that is exactly the train of thought I was on, however it is still not giving the desired result.

I just put a log call in the update function of pControls.js and it is never getting called at all. That is the new confusing bit.

I will try your active property suggestion and see how we go, and I might give the chrome debugger a serious work out this afternoon.

Thanks for all your help!

8 years ago by Joncom

I might give the chrome debugger a serious work out this afternoon.
Nice. Yeah, setting break points and following the flow of your code, step-by-step, and making sure that things happen the way you expect, is a great idea :)

8 years ago by BennyT

Hi all that are reading this for the first time and have helped in the past.

Unfortunately I still have not made any progress.

The latest update is that I am not using a separate entity and I have moved all the logic inside the single teammate entity to try and discover the bug with no help.

I am getting the exact same result as before with it doubling up.

I have been playing around with the breakpoints in Google Chrome Debugger and it hasn't really helped.

Here is the entire teammate entity class for your reference.

Any help would be greatly appreciated after on and off attempts at this. Thanks!

ig.module(
	'game.entities.teammate'
) 
.requires(
	'impact.entity'//,
	//'game.entities.pcontrols'
)
.defines(function(){
	EntityTeammate = ig.Entity.extend({
		name: "teammate", // needed for the impact-bootstrap camera
		animSheet: new ig.AnimationSheet( 'media/player.png', 35, 39),
		type: ig.Entity.TYPE.A,
		checkAgainst: ig.Entity.TYPE.B,
		collides: ig.Entity.COLLIDES.ACTIVE,

		size: {x: 35, y:39},
		flip: false,
		speed: 80,
		health: 10, // using this as the stamina variable
		//lives: 3, not needed, when have health/stamina
		nicerPath: true, // a* plugin attribute
		holdingBall: false,
		bounciness: 0,
		possession: false,
		controller: null,
		
		teamName: null,
		pathTimer: null, // a* plugin attribute

		// this is how the entity starts based on these parameters
		init: function( x, y, settings ) {
			this.parent( x, y, settings );

			// storing the team details in the teammate object
			this.playerNumber = settings['playerNumber'];
			this.teamName = settings['teamName'];
			this.controller = settings['controller'];
			this.pathTimer = new ig.Timer(2);

			// Add the animations
			this.addAnim( 'idle', 1, [1] );
			this.addAnim( 'run', 0.05, [1,2,3,4,5,6,7] );
			this.addAnim( 'left', 0.05, [2,3,4,5,6,7,8] );
			this.addAnim( 'right', 0.05, [3,4,5,6,7,8,9] );

			console.log(this.controller);

		},

		flock: function () {
			// Update it every second
			if(this.pathTimer.delta() > 0) {
				var team = ig.game.getEntityByName(this.teamName);
				if(this.playerNumber >= 1 ) {
					this.getPath(Math.round(team.playersInTeam[0].pos.x), Math.round(team.playersInTeam[0].pos.y), true);
				} else {
					this.getPath(Math.round(team.playersInTeam[this.playerNumber + 1].pos.x), Math.round(team.playersInTeam[this.playerNumber + 1].pos.y), true);
				}
				this.pathTimer.reset();
			}

			// Walk the path
			this.followPath(this.speed);

			// Update the animation
			this.currentAnim.gotoFrame(this.headingDirection);
		},

		switchPlayer: function() {	
			console.log("the player entity that has control: " + this.id);
		
			this.controller = "computer";
		 	var nextPlayer = ig.game.getEntitiesByType('EntityTeam')[0].findAvailablePlayer(this);
		 	nextPlayer.controller = 'player';
			console.log("this should be the next entity: " + this.id);
		},

		update: function() {

			// something is going wrong here, need to debug.
			if(this.controller === "computer") {
				this.flock();
			} else {
				// temp variable to track the ball
				var ball = ig.game.getEntityByName("ball");
				
				// general player movement code
				if( ig.input.state('up') ) {
				    this.vel.y = -this.speed;
				}
				else if( ig.input.state('down') ) {
				    this.vel.y = this.speed;
				}
				else if( ig.input.state('left') ) {
				    this.vel.x = -this.speed;
				}
				else if( ig.input.state('right') ) {
				    this.vel.x = this.speed;  
				}
				else // if no button is pressed, they should stop moving
				{
				    this.vel.x = 0;
				    this.vel.y = 0;
				}

				// if the player wants to pass right
				if(ig.input.pressed('passright') ) {
					// and the player also has possession of the ball
					if(this.possession === true) {
						this.possession = false;
						ball.owner = null;
						ball.update("right");
					}
				} 

				if (ig.input.pressed('passleft') ) {
					if(this.possession === true) {
						this.possession = false;
						ball.owner = null;
						ball.update("left");
					}
				}

				if (ig.input.pressed('switch') ) {
					console.log('pressed switch');
					//this.switchPlayer();
					console.log("the player entity that has control: " + this.id);
				
					this.controller = "computer";
				 	var nextPlayer = ig.game.getEntitiesByType('EntityTeam')[0].findAvailablePlayer(this);
				 	nextPlayer.controller = 'player';
					console.log("this should be the next entity: " + nextPlayer.id);
				}

				// make sure the animations for the sprite match the current state
				if( this.vel.y < 0 ) {
					this.currentAnim = this.anims.run;
				}else if( this.vel.y > 0 ) {
					this.currentAnim = this.anims.run;
				}else if( this.vel.x > 0 ) {
					this.currentAnim = this.anims.right;
				}else if( this.vel.x < 0 ) {
					this.currentAnim = this.anims.left;
				}else{
					this.currentAnim = this.anims.idle;
				}
			}

			// move! 
			this.parent();
		},

		handleMovementTrace: function( res ) {
		    if( res.collision.y && this.vel.y > 32 ) {
		        console.log("heavy hit");
		    }

		    // Continue resolving the collision as normal
		    this.parent(res); 
		},

		draw: function() {
			// possesion marker
			if(this.controller === "player") {
				// border/background
				ig.system.context.fillStyle = "rgb(0,0,0)";
				ig.system.context.beginPath();
				ig.system.context.rect(
				                (this.pos.x - ig.game.screen.x) * ig.system.scale, 
				                (this.pos.y - ig.game.screen.y - 8) * ig.system.scale, 
				                this.size.x * ig.system.scale, 
				                4 * ig.system.scale
				            );
				ig.system.context.closePath();
				ig.system.context.fill();

				// health bar
				ig.system.context.fillStyle = "rgb(255,0,0)";
				ig.system.context.beginPath();
				ig.system.context.rect(
				                (this.pos.x - ig.game.screen.x + 1) * ig.system.scale, 
				                (this.pos.y - ig.game.screen.y - 7) * ig.system.scale, 
				                ((this.size.x - 2) * 1) * ig.system.scale, 
				                2 * ig.system.scale
				            );
				ig.system.context.closePath();
				ig.system.context.fill();
			} 
        	this.parent();
		},
	});
});

8 years ago by Joncom

Try putting your "if switch-input pressed, give control to a new player" logic into your main.js update function...

If you leave it in your entity update function, there's a chance you'll pass control to an entity that will be updated right after the current entity, which would give you a "double execution" effect.

In a nut shell, entities are updated one after the other, and if you keep passing control to the "next" entity (the one about to be updated), then he will pass control too, because ig.input.pressed will return true for the entire frame.

8 years ago by BennyT

@Joncom - You are an absolute lifesaver!

Moving it to the main game method was definitely the solution to remove the doubling up of the calls.

From a best practice, I am sure it is not perfect, but now I can make real progress on the next part of the game, so I might have to come back to this and look to optimise it once I have worked out the remainder of the mechanics and put the old logic back in.

So I guess to improve the quality of the code, I would have to put some flag in to prevent the update method from firing again on the new entity.

8 years ago by Joncom

Glad to help.
So I guess to improve the quality of the code, I would have to put some flag in to prevent the update method from firing again on the new entity.
Maybe. I dunno. Updating "which entity is currently being controlled" in your main.js update function doesn't seem that dirty to me... :)

8 years ago by BennyT

Sorry for the late reply but have been caught up with some other things.

I just thought it was dirty in the sense I have been reading a lot about entity component structures and I should really make controllers a component that I can pass around other entities.

I have a new issue now that I will post about in a separate thread.
Page 1 of 1
« first « previous next › last »