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 Cavalier

Hi, I'm starting out a RTS using impact and I'd like to know where I can get a tutorial, tips or stuff like that.

1 decade ago by Graphikos

You'll have to be more specific on stuff you are needing for more relevant suggestions. You can cruise the forums for lots of snippets, plugins, etc. You can also check out the growing collection over at Point of Impact.

1 decade ago by Cavalier

Thanks for the link. I've been lurking around and found some other posts and tutorials that form bits of things i'll need, such as map scrolling and clicking events.
Might as well make this thread my little diary of progress.

Anyway, I've managed to make a working (but far from perfect) right-mouse click scrolling (thanks mainly to Matt Lautz's tuto http://www.mattlautz.us/impact-demos/map-scrolling-with-arrow-keys/)
//Deadzone in pixels
var dzonex = 30;
var dzoney = 20;
MyGame = ig.Game.extend({

//Inside update function
if(ig.input.state('rclick')){   
//Horizontal movement
if (ig.input.mouse.x > (ig.system.width/2)) {
    if (ig.input.mouse.x > (ig.system.width/2)+dzonex) {
     //Check right border
     if (ig.game.screen.x < this.rightBorder -30){
      ig.game.screen.x +=20;
     }
     else {//Avoid going out of bounds by setting back to this value
      ig.game.screen.x = this.rightBorder;
     }
    }
   } else {
    if (ig.input.mouse.x < (ig.system.width/2)-dzonex) {
     //Check left border
     if (ig.game.screen.x > 30){
      ig.game.screen.x -=20;
     }
     else {//Avoid going out of bounds by setting back to this value
      ig.game.screen.x = 0;
     }
    }
   }
   
   //Vertical movement
   if (ig.input.mouse.y > (ig.system.height/2)) {
    if (ig.input.mouse.y > (ig.system.height/2)+dzoney) {
     //Check right border
     if (ig.game.screen.y < this.bottomBorder -30){
      ig.game.screen.y +=20;
     }
     else {//Avoid going out of bounds by setting back to this value
      ig.game.screen.y = this.bottomBorder;
     }
    }
   } else {
    if (ig.input.mouse.y < (ig.system.height/2)-dzoney) {
     //Check left border
     if (ig.game.screen.y > 10){
      ig.game.screen.y -=20;
     }
     else {//Avoid going out of bounds by setting back to this value
      ig.game.screen.y =0;
     }
    }
   }

It works, but what i want and can't seem to figure it out is how to make a scrolling similar to weltmeister's, a hold-click and drag from the position clicked.
Another thing I'm trying is that, the farther away from the deadzone, the faster the scrolling. I can make a working one with a large chain of if-elses, but isn't there a better way to do it?

EDIT: Wow, less than 5 minutes after posting, I stumble upon this.
http://pointofimpactjs.com/snippets/view/13/click-and-drag-screen

1 decade ago by Cavalier

Ok, now I have no idea why my entity isn't moving when I click. I'm trying to follow sleenee's code (http://impactjs.com/forums/code/top-down-rpg-mouse-click-straightline-movement), but it's not working for me at all. I'm trying to adjust it, but I can't figure why the entity doesn't move despite the fact that the velocity is changing.

ig.module('game.entities.player').
requires('impact.entity').defines(
function() {
    //code
  EntityPlayer = ig.Entity.extend ({
  animSheet: new ig.AnimationSheet( 'media/charset/char1.png', 24, 32 ),
  maxVel: {x: 300, y: 300},
  health:10,
        
  update: function(){
   if (ig.input.pressed('lclick') && this.inFocus()) {
      console.log('clicked');
      ig.log('clicked');
      this.selected = true;
   } 
   if (ig.input.state('lclick') && this.selected) {
    this.targetx = ig.input.mouse.x + ig.game.screen.x;
    this.targety = ig.input.mouse.y + ig.game.screen.y;
    console.log('posX = '+ this.pos.x + 'posX = '+ this.vel.x);
    console.log('posY = ' + this.targetx + ' - ' + this.targety);
   }
   this.distancetotargetx = this.targetx - this.pos.x - 24 ;
   this.distancetotargety = this.targety - this.pos.y - 32 ;
   
   if (this.distancetotargetx > 1 ){this.vel.x = 40;}
   if (this.distancetotargetx < 1){this.vel.x = -40;}
   this.parent;
  },
        
  inFocus: function() {
   return ((this.pos.x <= (ig.input.mouse.x + ig.game.screen.x)) &&
    ((ig.input.mouse.x + ig.game.screen.x) <= this.pos.x + this.size.x) &&
    (this.pos.y <= (ig.input.mouse.y + ig.game.screen.y)) &&
    ((ig.input.mouse.y + ig.game.screen.y) <= this.pos.y + this.size.y)
  )},
  
  init: function(x, y, settings){
   this.size.y = 32;
   this.size.x = 24;
   this.addAnim('idle',0.5, [0,1,2]);
   this.addAnim('walkleft',0.5, [0,1,2,1,3]);
   this.currentAnim = this.anims.idle;
   this.parent(x, y, settings);
  }
    })
});

1 decade ago by Cavalier

Really, I need to learn better to read the whole topics. In the above, Burxat's code was fully working. Also, there seems to be something wrong with that player entity of mine, as it won't move at all.

Anyway, with a player2 entity everything worked smoothly and I even managed to select and unselect which entity should move, a simple variable changing between true and false when clicked over did the trick.

Collisions are something I need to look further now. PASSIVE is out of the question, as I don't want my units going through one another. FIXED seems to be the best option, though sometimes a moving unit will push the other. Still, I consider this a small milestone. Next up, buildings and training new units.
Thinking about a building entity that spawns N 'selector' entities adjacent to it, which will begin training. Gotta get a hang of timers for this, too.
http://pointofimpactjs.com/tutorials/view/7/using-impact-timers

EDIT 1: Timers are working and were rather simple. Spawning a new entity with a specific animation was also easy, thanks to Graphikos' snippet (http://pointofimpactjs.com/snippets/view/11/set-animation-of-entity-on-spawn)

1 decade ago by Cavalier

Ignore this, I was supposed to edit the earlier post, not make a new one

EDIT 24/04/2013: Ok, I'm almost finishing unit training from a building. My building superclass has the following method

showUnitFrames: function(){
			ig.game.spawnEntity( EntityCheapGuyFrame, (this.pos.x + this.size.x)+4 , this.pos.y , {ownerBuilding: this});
			ig.game.spawnEntity( EntityHeavyGuyFrame, (this.pos.x + this.size.x)+4 , this.pos.y + 18, {ownerBuilding: this} );
			ig.game.spawnEntity( EntityFastGuyFrame, (this.pos.x + this.size.x)+4 , this.pos.y + 36, {ownerBuilding: this});
			ig.global.selected=this;
		}

The *Frame entities' superclass is the following, defined inside the same building.js
EntityUnitFrame = ig.Entity.extend({
		size: {x: 16, y: 16},
		type: 'unitFrame',
		//Ideally, use a single file for all unit frames, so they only overwrite trainTime and frame
		animSheet: new ig.AnimationSheet('media/charset/unitframes.png', 17, 17 ),
		ownerBuilding: 0,
		trainTime: 1,
		init: function( x, y, settings ) {
			this.parent( x, y, settings );
			// Add the animations
			this.addAnim( 'cheap', 1, [0] );
			this.addAnim( 'medium', 1, [1] );
			this.addAnim( 'tank', 1, [2] );
			this.addAnim( 'fast', 1, [3] );
			this.addAnim( 'heavy', 1, [4] );
			// Switch animations on Init
			this.currentAnim = this.anims[this.frame];
			console.log('Owner Building: ' + this.ownerBuilding);
		},
		
		inFocus: function() {
			return ((this.pos.x <= (ig.input.mouse.x + ig.game.screen.x)) &&
				((ig.input.mouse.x + ig.game.screen.x) <= this.pos.x + this.size.x) &&
				(this.pos.y <= (ig.input.mouse.y + ig.game.screen.y)) &&
				((ig.input.mouse.y + ig.game.screen.y) <= this.pos.y + this.size.y)
		)},
		
		trainUnit: function() {
			this.ownerBuilding.timer = new ig.Timer(this.trainTime);
			this.ownerBuilding.training = true;
			console.log('Training started! Building: ' + this.ownerBuilding);
		},
		
		update: function() {
			if (ig.input.pressed('rclick')) {
				this.kill();
			}
			if (ig.input.pressed('lclick')){
				if (this.inFocus()){
					//kills all unitFrames and starts training
					var frames = ig.game.entities;
					var len = frames.length;
					for(var i = 0;i<len;i++){
						if ((frames[i].type=='unitFrame')){
							frames[i].kill();
						}
					}
					this.trainUnit();
				}
			}
			this.parent();
		}
	});

With the rest of the building code, whenever the building is NOT selected, no uFrames are shown. Clicking on a unit frame immediately starts the training on said building, which won't show frames while the timer is running.

Right now I'm thinking it would be a better idea to, instead of that many extending unit frames, it'd be better to have another EntityUnitFrame variable, namely 'thisTrains', which would indicate what unit should be spawned. I'm looking at it right now.

Some 6 hours later edit:
Well, I've managed to succesfully spawn new units assigned to a owner (player), which I'll use to check so the player doesn't move enemy units. Also created starting positions. Quite a lot for day in my book \o/
Anyway, a strange thing that happens with unit training is that, if there is no units of that type already present in the map, I'll get a Uncaught Can't spawn entity of type Entity%ttypehere% game.js:129
If I try and add said entity to the building's requires, I get
ig.module('game.entities.building')
.requires('impact.entity',
'game.entities.SuperUnit',
'game.entities.HeavyGuy')
.defines(function(){
    EntityBuilding = ig.Entity.extend({//...

//Error messages ▼
Uncaught Module 'game.entities.heavyGuy' is already defined impact.js:234

Uncaught Unresolved (circular?) dependencies. Most likely there's a name/path mismatch for one of the listed modules:
game.main (requires: game.levels.test1)
game.levels.test1 (requires: game.entities.building)
game.entities.building (requires: game.entities.HeavyGuy) impact.js:348

Another hour later edit: Turns out my problem was that I was messing the capitalization of the entities' names. I was trying to require 'game.entities.CheapGuy' instead of 'game.entities.cheapGuy', etc.

1 decade ago by RockLeo

I`m following your topic, very good info about RTS Games. Keep the good work!

1 decade ago by JamieRushworth

Please keep updating this thread with info, I am also making a top-down RTS type game and it's useful seeing other ways of doing things. I'll post anything I find that'll be useful also.

One question though, has anyone figured out a good system for zooming in and out? I can scale the canvas, but can't figure out how to make it work with impact's draw method properly; it doesn't clear the canvas. Not sure if there are any other un-wanted side effects either.

EDIT: Here is a more neat & tidy way to check if the mouse is colliding with something or not. It basically creates an entity for the mouse cursor so that you can use Impact's built in collision code. this.clickLoc can be used for the map dragging part also.

Code for a pointer entity:

ig.module(
	'game.entities.pointer'
)
.requires(
	'impact.entity'
)
.defines(function() {


	EntityPointer = ig.Entity.extend({
		name: 'pointer',
	    checkAgainst: ig.Entity.TYPE.A,
	    size: {x:1, y:1},
	    isClicking: false,
	    
	    update: function() {
	        // Update the position to follow the mouse cursor.
	        this.pos.x = ig.input.mouse.x + ig.game.screen.x;
	        this.pos.y = ig.input.mouse.y + ig.game.screen.y;
	        this.screenX = ig.input.mouse.x;
	        this.screenY = ig.input.mouse.y;

	        if (ig.input.pressed('click')) {
	        	//Save the co-ord of where you initially clicked
	        	//Allows you to drag and compare to initial click location
	        	this.clickLocX = this.pos.x;
	        	this.clickLocY = this.pos.y;
	        }
	    },
	    
	    check: function( other ) {
	        // User is clicking and the 'other' entity has a 'clicked' function?
	        if( 
	            ig.input.state('click') && 
	            typeof(other.clicked) == 'function' 
	        ) {
	            other.clicked();

	        }
	    }
	});
});

Then in your player file or wherever you want to detect if something has been clicked on, add this:

clicked: function() {

        //Add your click code here, this is an example of what I use
	this.selected = true;

}

1 decade ago by Cavalier

Hear ye, me faithful followers! :D
JamieRushworth, I saw this topic on the forum. Hopefully it can help you with zooming. Currently I don't plan on using that on mine, rather only the normal map and a minimap.
http://impactjs.com/forums/help/scaling-math-help-p

What I use to check whether or not an entity was clicked is this (http://pointofimpactjs.com/snippets/view/16/entity-click)
inFocus: function() {
   return ((this.pos.x <= (ig.input.mouse.x + ig.game.screen.x)) &&
    ((ig.input.mouse.x + ig.game.screen.x) <= this.pos.x + this.size.x) &&
    (this.pos.y <= (ig.input.mouse.y + ig.game.screen.y)) &&
    ((ig.input.mouse.y + ig.game.screen.y) <= this.pos.y + this.size.y)
  )},

update: function(){
  if ('leftclick' && this.inFocus) {
    //this was clicked over
}
 //without the left click, it'll enter whenever you hover the mouse over said entity
}

For dragging the map, I think Graphikos' snippet is pretty nice, since it makes just a few simple checks
http://pointofimpactjs.com/snippets/view/13/click-and-drag-screen
I think it's always better to avoid using entities if you can use pure logic ;)

You guys may or may not have noticed, but I also posted some directions to make a selection rectangle here
http://impactjs.com/forums/code/selecting-entities-with-a-mouse-drawn-rectangle

Now I'm also gonna share my movement algorithms. I'm using dumb movement (straight line, no check for collision). This is a modified version of Burxat's code from here
http://impactjs.com/forums/code/top-down-rpg-mouse-click-straightline-movement#post20974
One of the main problems I kept having with his code was, if I deleted the keyboard if's, the units would fidget around the destination instead of properly arriving and stopping.

movement: function(){
   if( this.destinationx < 9999999 && this.destinationy < 9999999 ) {
   // Accounts for center of character's collision area
    this.distancetotargetx = this.destinationx - (this.pos.x + (this.size.x/2));
    this.distancetotargety = this.destinationy - (this.pos.y + (this.size.y/2));
    if( Math.abs(this.distancetotargetx) > 3 || Math.abs(this.distancetotargety) > 3 ) {
     if( Math.abs(this.distancetotargetx) > Math.abs(this.distancetotargety) ) {
     // Move right
      if( this.distancetotargetx > 0.6 ) {
		  this.vel.x = this.speed;
		  this.xydivision = this.distancetotargety / this.distancetotargetx;
		  this.vel.y = this.xydivision * this.speed;
		  this.currentAnim = this.anims.right;
      }else { // Move left
		  this.vel.x = -this.speed;
		  this.xydivision = this.distancetotargety / Math.abs(this.distancetotargetx);
		  this.vel.y = this.xydivision * this.speed;
		  this.currentAnim = this.anims.left;
      }
    }else {
    // Move down
      if( this.distancetotargety > 0.6 ) {
		   this.vel.y = this.speed;
		   this.xydivision = this.distancetotargetx / this.distancetotargety;
		   this.vel.x = this.xydivision * this.speed;
      }else { // Move up
       this.vel.y = -this.speed;
       this.xydivision = this.distancetotargetx / Math.abs(this.distancetotargety);
       this.vel.x = this.xydivision * this.speed;
       }
      }
    }else {
		this.vel.y = 0;
		this.vel.x = 0;
		this.destinationx = 99999999;
		this.destinationy = 99999999;
	}
	}
  },

I took out his conditions for animation, as those are best checked in a separate function which only compares current speeds.
Anyway, you should call this.movement() inside your unit class' update(), outside any if conditions.
To set the destination with a click, use this inside your unit's update
if (ig.input.pressed('lclick')){
		 this.destinationx = ig.input.mouse.x + ig.game.screen.x;
		 this.destinationy = ig.input.mouse.y + ig.game.screen.y;
	}

Now, moving a group of units is another thing. If you just leave that as is, all your units will have the same destination, and we don't want that, do we?
Well, I've managed to make the selected units form straight lines of 6. If their collision is set to ACTIVE, you'll see some pushing here and there. Mine's are passive as of now
moveAll:function(){
	var leader = ig.global.selected[0];
	var distx = leader.destinationx - (leader.pos.x + (leader.size.x/2));
	var disty = leader.destinationy - (leader.pos.y + (leader.size.y/2));
	var mousePosy = (ig.input.mouse.y + ig.game.screen.y);
	var mousePosx = (ig.input.mouse.x + ig.game.screen.x);
	var line = 0;
	var evenNumber = true;
	var coluna = 0;
	
	for (var j = 0; j < ig.global.selected.length ; j++) {
		var current = ig.global.selected[j];
		var posicao = 0;
		if (coluna > 5){coluna = 0; line++;}
		if (Math.abs(distx) > Math.abs(disty) ) {
			//6 posições Y diferentes
			posicao = (current.size.y * coluna);
/*The below IF is optional, it serves to put the leader in the middle and each unit to the left/right of his position*/
			if (evenNumber) {posicao = posicao * (-1); evenNumber = false;} else {evenNumber = true;}
			current.destinationy = mousePosy - posicao;
			current.destinationx = leader.destinationx - ((current.size.x + 6) * line);
		} else {
			//6 posições X
			posicao = (current.size.y * coluna) + 8;
			if (evenNumber) {posicao = posicao * (-1); evenNumber = false;} else {evenNumber = true;}
			current.destinationx = mousePosx - posicao;
			current.destinationy = leader.destinationy - ((current.size.y + 4) * line);
		}
		coluna++;
	}
  },

Damn, it took me hours to come up with that. You can possibly make it so the units form a V shape instead. Basically, you define a "leader" unit ()since i'm not too picky, it's the first one of the array_), discover whether his X or Y distance is bigger and form a vertical or horizontal line accordingly. I'm calling this inside the unit's click event (the same where I define the destination), so it ends being executed [unit] times.

And some screenshots! I'm using a template for units, so they all look the same
Building's trainable units: http://anonmgur.com/up/3f87fc500d667b4abd45baa5d180c658.png

Draggin the mouse: http://www.anonmgur.com/up/08953c47109c0c4aab23839117d70789.png
And they're selected! http://www.anonmgur.com/up/bbcc3d648ab68f6ac1416c0d64ad2ae8.png

Pretty formation lines: http://anonmgur.com/up/e6e6a4b7a3f98dd76037a118edfaea16.png

Anyway, two things I've been searching like mad are a proper minimap (which shows revealed terrain as well as entities) and fog of war. dungeonmaster's game Flock Lord has those EXACTLY the way I want
http://impactjs.com/forums/help/iframe-kongregate-key-press-problem

1 decade ago by Cavalier

Well, i've managed to do something i've been wanting for some time!
Using a single tileset image and background should enable one to generate random maps of the specified size.

Tilefactor should vary according to the number of tiles you plan on using. Since I only plan on using 2 tiles for each map set (grass, dirt, rocky, snow and city), it should be either 0, 2, 4, 6 and 8.

By the way, you should call this inside your main.js. Right now I call it inside the init(), so it only runs once, otherwise you'd have a crazy map always changing. Well, new feature idea! Set a timer so that the map changes after X minutes!
generateMap: function(rows, columns){
		var maparray = [];
		var rowarray = [];
		var colmaprow = [];
		var colmap = [];
		var tilefactor = 2;
		var tilesize = 32;
		var collision = 0;
		this.mapheight = 50;
		this.mapwidth = 50;
		
		for (var y = 0; y < this.mapheight; y++) {
			for (var x = 0; x < this.mapwidth; x++) {
				if ((y > 0 && y < this.mapheight - 1) && 
					rowarray.length > 0 && rowarray.length < this.mapwidth - 1) {
					var tile = Math.random();
					if (tile > 0.89) {
						//Collision background
						collision = 1;
						tile = 2+tilefactor;
					} else {
						//Not collision
						collision = 0;
						tile = 1+tilefactor;
					}
					rowarray.push(tile);
					colmaprow.push(collision);
				} else {
					//Borders are black with collisions
					rowarray.push(0);
					colmaprow.push(1);
				}
			}
			maparray.push(rowarray);
			colmap.push(colmaprow);
			var rowarray = [];
			var colmaprow = [];
		}
		this.collisionMap = new ig.CollisionMap( tilesize, colmap );
        var bgmap = new ig.BackgroundMap( tilesize, maparray, this.maptile );
		this.backgroundMaps.push(bgmap);
//I use these for map scrolling boundaries, so the camera doesn't stray away
		this.rightBorder = ig.game.backgroundMaps[1].pxWidth - ig.system.width;
		this.bottomBorder = ig.game.backgroundMaps[1].pxHeight - ig.system.height;
		ig.global.rightBorder = ig.game.backgroundMaps[1].pxWidth - ig.system.width;
		ig.global.bottomBorder = ig.game.backgroundMaps[1].pxHeight - ig.system.height;
	},

It took me some time to figure out how to finally convert the Drop game's into this. Shouldn't be too hard to, for instance, allow for 6 different tiles for each set.
Making sure that the starting positions have a minimum size without collisions shouldn't be hard either. I'll see if I can come up with something

Now to work on the HUD and main menu

1 decade ago by JamieRushworth

Thanks for keeping this updated, I haven't contributed anything yet since I have been busy for the last week or so and haven't been working on the game. However I'm back at it today so when I finally get the zooming figured out I shall post it here for anyone that needs it.

The way I figure I could get it to work is by dynamically changing the canvas size and impact scale at the press of a button to give the game 2 zoom levels. So for example if I have a game that is normally 300x300 with a scale of 1 and want to zoom out, I could double the canvas size and halve the scale. I've just got to work out how I could do this with impact.

EDIT: Okay, so far I have figured out that I can use ig.system.resize(); to change the resolution and scale of the game, it just causes the graphics to be wrong which I assume is to do with how impact loads the game, so I think I need to make impact load both zoom levels when the game launches. Gonna jump into the game engine files now.

1 decade ago by JamieRushworth

Hooray, I finally figured out the zooming. It turns out that all the code I need was sitting in the weltmeister.js -_-.

All the code is in the main.js for the game. The relevant bits are:

//Inside the update function
if (ig.input.state('zoom out')) {

        //Change to 1 for 'zoom in' in a different if statement
	zoomLevel = 0.5;

	for( var i in ig.Image.cache ) {
		ig.Image.cache[i].resize( zoomLevel );
	}

	this.resize();

};

resize: function() {
	ig.system.resize(320 / zoomLevel,480 / zoomLevel, zoomLevel);
	this.draw();
},

Then just before you call ig.main at the bottom of the main.js file insert this:

ig.Image.inject({
	resize: function( scale ) {
		if( !this.loaded ) { return; }
		if( !this.scaleCache ) { this.scaleCache = {}; }
		if( this.scaleCache['x'+scale] ) {
			this.data = this.scaleCache['x'+scale];
			return;
		}
		
		// Retain the original image when scaling
		this.origData = this.data = this.origData || this.data;
		
		if( scale > 1 ) {
			// Nearest neighbor when zooming in
			this.parent( scale );
		}
		else {
			// Otherwise blur
			var scaled = ig.$new('canvas');
			scaled.width = Math.ceil(this.width * scale);
			scaled.height = Math.ceil(this.height * scale);
			var scaledCtx = scaled.getContext('2d');
			scaledCtx.drawImage( this.data, 0, 0, this.width, this.height, 0, 0, scaled.width, scaled.height );
			this.data = scaled;
		}
		
		this.scaleCache['x'+scale] = this.data;
	}
});

EDIT: Even better, if you want progressive zooming then you can use this:


if (ig.input.state('zoom in')) {

	if(zoomLevel <= 0.975) {
		zoomLevel += 0.025;
	}

	else {
		zoomLevel = 1;
	}

	for( var i in ig.Image.cache ) {
		ig.Image.cache[i].resize( zoomLevel );
	}

	this.resize();

};

if (ig.input.state('zoom out')) {

	if(zoomLevel >= 0.275) {
		zoomLevel -= 0.025;
	}

	else {
		zoomLevel = 0.25;
	}

	for( var i in ig.Image.cache ) {
		ig.Image.cache[i].resize( zoomLevel );
	}

	this.resize();

};

NOTE: This will only work if you are using a image based game. Tile-based games will looked gridded if you try to resize the game to anything that isn't 0.125, 0.25, 0.5, 1, 2, 4 etc

1 decade ago by Cavalier

That's really nice Jamie :D
Lately I haven't had much time to work on the coding per se, since this is my final project at the university I also need to make all the formal documentation. Right now, only thing left is draw architecture diagrams, showing how Impact interacts with HTML5/Canvas.
Hopefully my next update will be with adding a proper game settings for skirmish play + building that generates resources (game money)
Page 1 of 1
« first « previous next › last »