I am working on a clone of the game Spacewar!. In the game, ships can travel to the edge of the map and wrap around to the other side, at times split in half, one half on each side of the screen (when the ships go into the corners they are split in four). You can play the game here.
Right now I am using the modulus operator to wrap a div
around the screen, but it doesn't split the block in half like I would it to. Is there any way this is possible in JavaScript or jQuery?
Interesting link you have provided. They have implemented a full CPU emulation and running the game written in assembly.
Improving the modulo
Anyway if you are using the canvas to render sprites (images) the easiest way is a simple modulo with a modification to handle negative values.
The normal modulo breaks down when using negative values.
x = 100 % 200; // 100
x = 300 % 200; // 100
x = -100 % 200; // -100 the wrong sign should be 100
x = -50 % 200; // -50 wrong sign wrong direction should be 150
You need the modulo to always return a positive value in the correct direction. To handle the negative number do the modulo twice, the first will get within the range you want but +/- the range. Then make the negative positive by adding the range. Then just do the modulo again.
var range = 200;
var x = 150;
x = ((x % range) + range) % range; // result 150
x = -50;
x = ((x % range) + range) % range; // result 150 correct.
Simple wrapping
Using the above modulo algorithm it is then a simple matter to check boundaries and render the sprite as needed.
// assuming ctx is canvas context 2D
// canvas is the canvas.
// img is a HTMLImageElement
var canW = canvas.width; // the canvas size
var canH = canvas.height;
// draw a sprite that is wrapped around the edges of the canvas
function drawWrappedSprite(img,x,y){
x1 = ((x % canW) + canW) % canW; // wrap around correcting for negative values
y1 = ((y % canH) + canH) % canH;
x2 = x1 + img.width; // get right and bottom
y2 = y1 + img.height;
ctx.drawImage(img, x1, y1); // draw first copy
if(x2 > canW){ // check if touching right side
ctx.drawImage(img, x1 - canW, y1); // draw left copy
if(y2 > canH){ // check if touching bottom
ctx.drawImage(img, x1 - canW, y1 - canH); // draw top copy
}
}
if(y2 > canH){
ctx.drawImage(img, x1 , y1 - canH); // draw top copy
}
}
Wrapping rotated sprites
As the game has sprites that are rotated the above function will not work as the rotation will change the size of the sprite. To handle the rotated sprite you need to check the max size it can be. This is the length of the diagonal line across the sprite which can be found with sqrt(width * width + height * height)
Assuming that you want the sprite rotated about its center you can find the sprite max extent (top,bottom,left and right) by subtracting and adding half the diagonal to the x,y center position. The just as the first function, do the modulo and draw the sprite as needed.
There will be some situations where the sprite is drawn on the opposite side even though it is not visible. If you are drawing lots of sprites (100+) you may want to get the exact extent rather than the max extent but then you will have to transform at least one corner of the sprite to get the horizontal and vertical extent. Then just use those values rather than diag
as in the function.
// assuming ctx is canvas context 2D
// canvas is the canvas.
// img is a HTMLImageElement
var canW = canvas.width; // the canvas size
var canH = canvas.height;
// draws sprite rotated about its center by r
function drawWrappedRotatedSprite(img,x,y,r){ // x,y center pos, r rotation in radians
var diag = Math.sqrt(img.width * img.width + img.height * img.height); // get diagonal size
diag /= 2; // half the diagonal
x1 = (((x - diag) % canW) + canW) % canW; // get left extent position and
y1 = (((y - diag) % canH) + canH) % canH; // wrap around correcting for negative values
var w = - img.width / 2; // get image width height
var h = - img.height / 2 // offset in rotated space
x2 = x1 + diag * 2; // get right and bottom max extent
y2 = y1 + diag * 2;
ctx.setTransform(1,0,0,1,x1 + diag, y1 + diag); // set origin
ctx.rotate(r); // set rotation
ctx.drawImage(img, w, h); // draw image rotated around its center
if(x2 > canW){ // check if touching right side
ctx.setTransform(1,0,0,1,x1 + diag - canW, y1 + diag); // set origin
ctx.rotate(r); // set rotation
ctx.drawImage(img,w,h); // draw image rotated around its center
if(y2 > canH){ // check if touching bottom
ctx.setTransform(1,0,0,1,x1 + diag - canW, y1 + diag - canH); // set origin
ctx.rotate(r); // set rotation
ctx.drawImage(img,w,h); // draw image rotated around its center
}
}
if(y2 > canH){
ctx.setTransform(1,0,0,1,x1 + diag, y1 + diag - canH); // set origin
ctx.rotate(r); // set rotation
ctx.drawImage(img,w,h); // draw top image rotated around its center
}
ctx.setTransform(1,0,0,1,0,0); // reset the transform (should only do this after all sprites
// using this function have been drawn
}