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 Chmood

I'm encountering one issue while experimenting with canvas that resize on the fly.

In short, when the pixel scaling ratio vary, all bitmaps already loaded don't adapt to this new ratio.

See my problem here : http://flipbackstudio.com/000/canvas_resize/

(resize your browser window, the "game zone" keep correct, but pics are weird. Hit F5 and all is reloaded with the new pixel ratio, and fine looking)

My canvas is resized with :

ig.system.resize( windowWidth, windowHeight, pixelScale);

What else do I need to update (I bet I must replay images assets loading routine, scaled with the new pixel ratio). Any hints ?

Thanks by advance :p

I'm working on a "blank" impact template, scaled for my (future) needs.

The goal was to fulfill the browser viewport with the game canvas, instead of having a smaller canvas centered over a css background (for instance).

The canvas should update its size on window.resize event, while keeping a predefined ratio (in my case 3:2).

All seems fine, this image re-processing issue excepted...

1 decade ago by Arantor

The images are scaled when they're first loaded so that you don't have any performance hits doing it later. When you do the resize, the images won't be rescaled, and they won't rescake cleanly if the scale factor isn't an integer anyway...

You may have to do it directly be applying the draw directly at the canvas level (refer to the various documentation on the web for drawing directly to a canvas using its context)

1 decade ago by Chmood

Couldn't I set a custom event, launched when pixelrate (as an integer of course) changes ?

And then catching this event, looping through all images of the game, calling their respective ig.image.resize() functions and so on...

(Writing this makes me think that impact doesn't seem to keep any cached version of unscaled bitmaps, that would fail the upper idea. I should then cache them all just after loading, and just before initial scaling...)

By the way, why to draw directly on canvas ? All I need is scale-updated pics, is that so complicated to achieve ?

1 decade ago by Graphikos

Of course you can draw directly to the canvas. Just work with the canvas context.

ig.system.context

Edit: Oh, I misread, you are asking "why".

1 decade ago by Chmood

Thanks to Graphikos, I found the ig.resources var, containing all games assets.
Not yet working, I still have to retrieve ig.image's from it, and then call their respective (modified) resize() methods.

Here is the problematic code I have for now :


	// resize  all bitmap assets
	var res = ig.resources;
	console.log('Resources = ' + res.length + ' elements');
	res.forEach(function(img) {

	//	TODO : reload original, non-scaled images
	//	img.resize();					// BUG : img is not an image !!!
		console.log(img);
	});

And the full gameResize() function (which is called just after the ig.main() of the game, and when 'resize' or 'orientationchange' window events are triggered) :


resizeGame = function() {

	// Size of the viewport (in px)
	var windowWidth = window.innerWidth,
		windowHeight = window.innerHeight;
	// Viewport's ratio and Game ratio
	var windowRatio = windowWidth / windowHeight,
		gameRatio = CONFIG.GAME.WIDTH / CONFIG.GAME.HEIGHT;

	// Check ratios, and get pixelScale
	var ps;
	if (windowRatio > gameRatio) {
		ps = Math.floor( windowHeight / (CONFIG.GAME.HEIGHT * CONFIG.GAME.TILE_SIZE) ); // too wide
	} else {
		ps = Math.floor( windowWidth / (CONFIG.GAME.WIDTH * CONFIG.GAME.TILE_SIZE) ); 	// too high
	}

	// Check if pixelScale has changed
	if (this.pixelScale != ps ) {
		console.log('pixelScale changed from ' + this.pixelScale + ' to ' + ps);
		this.pixelScale = ps;

		// resize  all bitmap assets
		var res = ig.resources;
		console.log('Resources = ' + res.length + ' elements');
		res.forEach(function(img) {
		//	img.resize();					// BUG : img is not an image !!!
			console.log(img);
		});
	}

	// Final resizing
	windowWidth = CONFIG.GAME.WIDTH * CONFIG.GAME.TILE_SIZE;
	windowHeight = CONFIG.GAME.HEIGHT * CONFIG.GAME.TILE_SIZE;

	ig.system.resize( windowWidth, windowHeight, this.pixelScale);

	console.log('Pixel ratio [X' + pixelScale + '] Game size [' + windowWidth + 'x' + windowHeight + ']' + ' Screen size [' + windowWidth*pixelScale + 'x' + windowHeight*pixelScale + ']');
}

Thanks a lot for the replies !

1 decade ago by Chmood

Update of the day : http://flipbackstudio.com/000/canvas_resize_01/

Instructions :

- resize your browser window, then hit the X key. Canvas and bitmaps should resize to the maximum space available, though keeping the game ratio unchanged.

- compared to last iteration, you don't have to refresh the whole page to make the scaling occur !

Real bugs are : my function doesn't work when fired with window.addEventListener(), in short.

Many ig.game class variables get undefined, don't know why and still wondering.

Beside of this, for now I just call manually the bitmap resizing on my two assets (one font, one tileset), but the goal is to update ALL game assets in a raw...

Keep in touch !

1 decade ago by Chmood

Added a looping parallax scrolling, and fixed many bugs.

http://flipbackstudio.com/000/canvas_resize_02/

Now the algo scales from 1 to 16 times, with hqx filtering when possible. Higher scaling is fallbacked to the only nearest neighbour filter.

Next, some entities will be spawned over it, to see if scaling occurs correctly.
And still much ugly code to clean...

My todo list :

- fix window.events firing
- fix HQX before NN bug (only NN before HQX for now)
- serialise ig.image's resize() function
- enable preRender for backgroundMaps
- add ppi support

1 decade ago by Chmood

One big step beyond, the window resizing now triggers the canvas update, no more need to press the X key.

Watch it in action : http://flipbackstudio.com/000/canvas_resize_03/

Big, big thanks to Graphikos on the irc channel, who helped me a lot, despite of my awfull coding abilities...

New features :

- Some animated background tiles (water flooding), made possible with Dominic wise advices and (custom) code example.
Why animated tiles you'll ask ? Because I totally broke down the ig.backgroundMap.preRender feature, as chunks are not (yet) updated by my gameResize() function. I will try to fix it with the second background layer (that will remain un-animated)...

- I added a source/ folder, where you can find all media/ and lib/game/ files. By adding your own copy of impact engine, you can run the project locally.

=> http://flipbackstudio.com/000/canvas_resize_03/source/

The lib/impact/image.js (whose resize() function and this.dataUnscaled member has been modified a lot) is also available. Let me know if it's a problem, I can only release the modified parts.

1 decade ago by Graphikos

You don't have to hack up the actual Impact source code to make changes. This is where inject comes in.

http://impactjs.com/documentation/class-reference/class#inject

1 decade ago by Chmood

Gosh I knew it, I'll have to rewrite many things.
I forgot about this (helpful) doc page.

Keeping impact core files intact is part of my goals.

As a reminder, this project is by now only a proof of concept, once again in order to see what the engine is capable of.

It could end into a little HQX plugin (with lib/impact/image.js inject overwriting), plus a bigger one, holding all the dynamic-pixelrate related things inside of it.

As long as the pixelrate doesn't change during the game, things are easy as loaded bitmaps always fits well.

Thanks for the tip, anyway...

1 decade ago by FelipeBudinich

That's beautiful, I've always loved HQX scaling.

Currently I'm working on a high res game, and I also needed dynamic scaling, tho I ended up doing this outside of the game:

		dynamicScaling: function(){
					 
			var finalWidth = 1024; //the default width of my game
			var finalHeight = 712; //the default height of my game

			window.ScaleY = 1;
			window.ScaleX = 1;
			
			if (finalHeight > window.innerHeigh){
				finalHeight = window.innerHeigh;
				window.ScaleY = finalHeight / 712;
			}
			
			if (finalWidth > window.innerWidth){
				finalWidth = window.innerWidth;
				window.ScaleX = finalWidth / 1024;
			}
			document.getElementById('canvas').getContext('2d').scale(window.ScaleX, window.ScaleY)

		}

I had to modify the mousemove function inside input.js, so thanks for reminding me of .inject Graphikos! (now there's so much I got to overwrite and re-write hehe)

Something like this, coupled with HQX would look beautiful!

1 decade ago by paulh

I tried to download the code, but the resize.js link didnt work :-(

1 decade ago by Chmood

Sorry for this, I didn't notice it before.

Sadly, I couldn't find the sources for this project anymore.
All I came across was a kind of plugin, names dynscale.js (instead of game-resize.js).
Some modifications are needed, consequently. (EDIT : making filenames match shoud be enough).

Heres the stuff :


 /* 
/// DYNAMIC PIXEL SCALING PLUGIN
*/


ig.module( 'plugins.dynscale' )

.requires(

	'impact.impact',
	'impact.game'
)

.defines(function(){

//ig.dynscale = ig.Class.extend({});

ig.Game.inject({

	init: function() {

		this.resizeGame();
		this.parent();
	},
	
});


ig.Image.inject({

	dataUnscaled: null,
	widthUnscaled: 0,
	heightUnscaled: 0,


	onload: function( event )  {
		this.widthUnscaled  = this.data.width;
		this.heightUnscaled = this.data.height;
		this.dataUnscaled = this.data;

		this.parent( event );
	},


    resize: function( scale ) {
/*        if (scale>1 && scale<5) {
            this.data = hqx(this.data, scale);
        } else {
            this.parent( scale );   
        }
*/
		this.resizeNearestNeighbor(scale);
    },


	resizeNearestNeighbor: function (scale) {
		// Nearest-Neighbor scaling
		
		// The original image is drawn into an offscreen canvas of the same size
		// and copied into another offscreen canvas with the new size. 
		// The scaled offscreen canvas becomes the image (data) of this object.

		var w = this.widthUnscaled;
		var h = this.heightUnscaled;

/*		if (cascade) {
			w = this.width;
			h = this.height;
		}
*/
		var widthScaled = w * scale;
		var heightScaled = h * scale;

		var orig = ig.$new('canvas');
		orig.width = w;
		orig.height = h;
		var origCtx = orig.getContext('2d');
		origCtx.drawImage(this.dataUnscaled, 0, 0, w, h, 0, 0, w, h);
		var origPixels = origCtx.getImageData(0, 0, w, h);

		var scaled = ig.$new('canvas');
		scaled.width = widthScaled;
		scaled.height = heightScaled;
		var scaledCtx = scaled.getContext('2d');
		var scaledPixels = scaledCtx.getImageData(0, 0, widthScaled, heightScaled);
		for (var y = 0; y < heightScaled; y++) {
		    for (var x = 0; x < widthScaled; x++) {
			var index = ((y / scale).floor() * w + (x / scale).floor()) * 4;
			var indexScaled = (y * widthScaled + x) * 4;
			scaledPixels.data[indexScaled] = origPixels.data[index];
			scaledPixels.data[indexScaled + 1] = origPixels.data[index + 1];
			scaledPixels.data[indexScaled + 2] = origPixels.data[index + 2];
			scaledPixels.data[indexScaled + 3] = origPixels.data[index + 3];
		    }
		}
		scaledCtx.putImageData(scaledPixels, 0, 0);

		this.data = scaled;
	},






});


}); // end define

The NN scaling routine is redundant with the same code (uglily splashed) in /lib/impact/image.js.

I frankly cannot remember how I came up with this...

In fact the trick was to double interpolate images when possible : first with NN, then with HQX.

In images.js :

	resize: function (scale) {

		if (scale == 1 ) {		
			this.data = this.dataUnscaled;
			return;		}//TODO (now we can resize back to 1)

		if (CONFIG.EFFECTS.HQX) {
			switch( scale ) {
			case 2:		this.resize_hqx(scale); break;
			case 3:		this.resize_hqx(scale); break;
			case 4:		this.resize_hqx(scale); break;
			case 5:		this.resize_nearest_neighbor(2.5); 	this.resize_hqx(2, true); break;	// non integer ratio
			case 6:		this.resize_nearest_neighbor(2); 	this.resize_hqx(3, true); break;
			case 7:		this.resize_nearest_neighbor(3.5); 	this.resize_hqx(2, true); break;	// non integer ratio
			case 8:		this.resize_nearest_neighbor(2); 	this.resize_hqx(4, true); break;
			case 9:		this.resize_nearest_neighbor(3); 	this.resize_hqx(3, true); break;
			case 10:	this.resize_nearest_neighbor(5);	this.resize_hqx(2, true); break;
			case 11:	this.resize_nearest_neighbor(5.5);	this.resize_hqx(2, true); break;	// non integer ratio
			case 12:	this.resize_nearest_neighbor(3);	this.resize_hqx(4, true); break;
			case 13:	this.resize_nearest_neighbor(6.5);	this.resize_hqx(2, true); break;	// non integer ratio
			case 14:	this.resize_nearest_neighbor(3.5);	this.resize_hqx(4, true); break;	// non integer ratio
			case 15:	this.resize_nearest_neighbor(5);	this.resize_hqx(3, true); break;
			case 16:	this.resize_nearest_neighbor(4);	this.resize_hqx(4, true); break;
			default:	this.resize_nearest_neighbor(scale);
			}
		} else {
			this.resize_nearest_neighbor(scale);
		}


The real problem was to scale entities : for those present on screen (and in ig.Ressources or something like this), it was ok, but I couln't access to entities not spawned in the game.

Exemple with the jump'n'run demo : player and monsters scaled nicely, but if no bullet on screen, then bullets didn't scaled at all.

Also was needed a kind of cache, to keep unscaled versions of each image.


Hope it helps a bit...

1 decade ago by SlouchCouch

I just started implementing fullscreen for my game but I like this way better than what I'm doing. when i get home from work today I'm going to tinker with it. A couple questions:
1. does this plugin work pretty much just like your examples posted from above? I know you mentioned you lost the source code initially, but it wasn't clear if the stuff you found did the job anyway.
2. has the prerendered background chunks issue been addressed? if not i'll probably jump in and try to fix it.
3. mind if i make a plugin and throw it on github with an mit license?

-dave
Page 1 of 1
« first « previous next › last »