Transparent canvas viewport
I want to make my game accessible so we can navigate the map. I tried to implement this code: https://jsfiddle.net/gfcarv/QKgHs/ , but it didn't work for me, I tried to solve it in different ways, I searched the web and couldn't find a simple viewport that can match my code ...
I just want the player to always be in the middle of the canvas, and when we click (or hold down) it moves the map, but each game object remains in its position. If it reaches either side of the map, just let it be done, it can move around the map. And I need a map, a simple image or some generated canvas rectangles or whatever.
This is my simplified code:
function randomNumberFromRange( min, max ) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function testCollisionRectRect( rectangle1, rectangle2 ) {
return rectangle1.x - rectangle1.width/2 <= rectangle2.x - rectangle2.width/2 + rectangle2.width
&& rectangle2.x - rectangle2.width/2 <= rectangle1.x - rectangle1.width/2 + rectangle1.width
&& rectangle1.y - rectangle1.height/2 <= rectangle2.y - rectangle2.height/2 + rectangle2.height
&& rectangle2.y - rectangle2.height/2 <= rectangle1.y - rectangle1.height/2 + rectangle1.height;
}
Array.prototype.remove = function() {
var what, a = arguments, L = a.length, ax;
while (L && this.length) {
what = a[--L];
while ((ax = this.indexOf(what)) !== -1) {
this.splice(ax, 1);
}
}
return this;
}; // Function to pop specific element drom array by value
function drawLine( startX, startY, endX, endY, color, width ) {
ctx.save();
ctx.strokeStyle = color;
ctx.lineWidth = width;
ctx.beginPath();
ctx.moveTo(startX,startY);
ctx.lineTo(endX,endY);
ctx.stroke();
ctx.restore();
}
function drawBorder( x, y, width, height, lineWidth, strokeColor ) {
ctx.save();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeColor;
ctx.strokeRect( x, y, width, height);
ctx.restore();
}
function drawRect( x, y, width, height, fillColor ) {
ctx.save();
ctx.fillStyle = fillColor;
ctx.fillRect( x, y, width, height );
ctx.restore();
}
// ====================
// Global variables
// ====================
var ctx = $("#canvas")[0].getContext('2d'),
canvas = document.getElementById('canvas'),
cHeight = canvas.height = 500,
cWidth = canvas.width = 500,
canvasOffset = $('#canvas').offset(),
offsetX = canvasOffset.left,
offsetY = canvasOffset.top,
frameCounter = 0,
enemyList = [],
spawningEnemies_FLAG = true,
isLeftMouseButtonHeld_FLAG = false,
cursors = ['default', 'pointer'],
enemiesOnMap = 100,
fps = 120,
mouseX,
mouseY;
// canvas settings
ctx.font = '22px Arial';
var sharedBehaviour = {
x: cWidth / 2,
y: cHeight / 2,
id: undefined,
type: 'entity',
width: 15,
height: 15,
fillColor: '#E15258',
targetX: null,
targetY: null,
bulletSpeed: 1,
speed: 1,
update( type ) {
// if there is target
if( this.targetX !== null ) {
// Find out distance to target
var distanceX = this.targetX - this.x; // distance on X axis
var distanceY = this.targetY - this.y; // distance on Y axis
var distanceToTarget = Math.sqrt( distanceX*distanceX + distanceY*distanceY ); // distance
// If distance is smaller or equal speed, then just set position
if( distanceToTarget <= this.speed ) {
this.x = this.targetX;
this.y = this.targetY;
// Then reset
this.targetX = this.targetY = null;
} else { // If distance is bigger than speed, so we want to move with speed
distanceX = distanceX / distanceToTarget;
distanceY = distanceY / distanceToTarget;
distanceX = distanceX * this.speed;
distanceY = distanceY * this.speed;
this.x += distanceX;
this.y += distanceY;
}
}
},
draw( type ) {
drawRect( this.x - this.width/2, this.y - this.height/2, this.width, this.height, this.fillColor );
},
isColliding( entity ) {
return testCollisionRectRect( this, entity );
}
};
var player = Object.assign({}, sharedBehaviour, {
x: cWidth/2 - 12.5,
y: cHeight/2 - 12.5,
id: 980722,
type: 'player',
width: 25,
height: 25,
fillColor: '#82d877',
speed: 2.5,
isWithinRange( entity, val ) {
// Check if enemy is on player
var distanceX = this.x - entity.x;
var distanceY = this.y - entity.y;
return Math.sqrt( distanceX*distanceX + distanceY*distanceY ) < val;
}
});
function createEnemy( x, y, type, width, height, fillColor, speed, name ) {
var newEnemy = Object.assign({}, sharedBehaviour, {
x,
y,
type,
width,
height,
name,
fillColor,
speed,
setTarget( entity ) {
this.targetX = entity.x + randomNumberFromRange(-50, 50); // Set X with some random number
this.targetY = entity.y + randomNumberFromRange(-50, 50); // Set Y with some random number
},
isOnPlayer( val ) {
// Check if enemy is on player
var distanceX = player.x - this.x;
var distanceY = player.y - this.y;
return Math.sqrt( distanceX*distanceX + distanceY*distanceY ) < val;
},
isMouseOver( ) {
return (this.mouseX + this.width/2 >= this.x && this.mouseX + this.width/2 <= this.x + this.width && this.mouseY + this.height/2 >= this.y && this.mouseY + this.height/2 <= this.y + this.height);
}
});
enemyList.push( newEnemy );
}
function newGame( ) {
player.x = cWidth/2 - 12.5;
player.y = cHeight/2 - 12.5;
frameCounter = 0;
enemyList = [];
spawningEnemies_FLAG = true;
isLeftMouseButtonHeld_FLAG = false;
// Spawning enemies
for( i=0; i < randomNumberFromRange(15, 25); i++ ) {
createEnemy( randomNumberFromRange(0, cWidth), randomNumberFromRange(0, cHeight), 'passive', randomNumberFromRange(12, 18), randomNumberFromRange(12, 18), 'red', 1, 's' );
createEnemy( randomNumberFromRange(0, cWidth), randomNumberFromRange(0, cHeight), 'agressive', randomNumberFromRange(10, 16), randomNumberFromRange(10, 16), 'lightblue', 1.5, 'l' );
}
update();
}
function update( ) {
frameCounter++;
ctx.clearRect(0,0,cWidth,cHeight);
// ========== Update ==========
// Player
player.update('player');
// Enemies
enemyList.forEach( enemy => { enemy.update(); });
// ========== Draw ==========
// Enemies
enemyList.forEach( enemy => { enemy.draw(); });
// Player
player.draw('player');
// ========== Conditions ==========
// Enemies
// Behaviour
enemyList.forEach( enemy => {
if ( Math.random() < ( 1 / 15 ) ) {
if ( enemy.isOnPlayer( player.circleRadius ) ) {
if ( ! (enemy.isOnPlayer( 50 )) ) {
enemy.setTarget( player );
}
}
}
if ( Math.random() < ( 1 / 800 )) {
if ( ! (enemy.isOnPlayer( player.circleRadius )) ) enemy.setTarget( enemy );
}
if ( enemy.isOnPlayer(player.circleRadius/2 - 25 ) ) {
enemy.targetX = enemy.targetY = null;
}
( enemyList.length === enemiesOnMap ) ? spawningEnemies_FLAG = false : spawningEnemies_FLAG = true;
});
setTimeout(function() { requestAnimationFrame(update); }, 1000 / fps);
}
function setPlayerTarget_and_checkPlayerPosition( mouse ) {
player.targetX = mouseX;
player.targetY = mouseY;
if (player.targetX < player.width/2) player.targetX = player.width/2;
if (player.targetX > cWidth - player.width/2) player.targetX = cWidth - player.width/2;
if (player.targetY < player.height/2) player.targetY = player.height/2;
if (player.targetY > cHeight - player.height/2) player.targetY = cHeight - player.height/2;
}
canvas.addEventListener('mousedown', function( mouse ) {
isLeftMouseButtonHeld_FLAG = true;
setPlayerTarget_and_checkPlayerPosition(mouse);
});
canvas.addEventListener('mouseup', function( mouse ) {
isLeftMouseButtonHeld_FLAG = false;
});
canvas.addEventListener('mousemove', function( mouse ) {
if( isLeftMouseButtonHeld_FLAG ) {
setPlayerTarget_and_checkPlayerPosition(mouse);
}
});
canvas.addEventListener('mousemove', function( mouse ) {
mouseX = parseInt(mouse.clientX - offsetX);
mouseY = parseInt(mouse.clientY - offsetY);
});
newGame();
canvas { border: 1px solid black; background-color: white; }
<canvas id="canvas"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
source to share
Moving the viewport to keep the player centered is pretty straightforward:
You can just use ctx.setTransform(1, 0, 0, 1, centerX - player.x, centerY - player.Y)
;
Now with your current mouse logic, you really need an object viewPort
with two properties x
and y
so that you can get the correct distance between the cursor and your player.
You also obviously need to remove the constraints you met in setPlayerTarget_and_checkPlayerPosition
.
function randomNumberFromRange( min, max ) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function testCollisionRectRect( rectangle1, rectangle2 ) {
return rectangle1.x - rectangle1.width/2 <= rectangle2.x - rectangle2.width/2 + rectangle2.width
&& rectangle2.x - rectangle2.width/2 <= rectangle1.x - rectangle1.width/2 + rectangle1.width
&& rectangle1.y - rectangle1.height/2 <= rectangle2.y - rectangle2.height/2 + rectangle2.height
&& rectangle2.y - rectangle2.height/2 <= rectangle1.y - rectangle1.height/2 + rectangle1.height;
}
Array.prototype.remove = function() {
var what, a = arguments, L = a.length, ax;
while (L && this.length) {
what = a[--L];
while ((ax = this.indexOf(what)) !== -1) {
this.splice(ax, 1);
}
}
return this;
}; // Function to pop specific element drom array by value
function drawLine( startX, startY, endX, endY, color, width ) {
ctx.save();
ctx.strokeStyle = color;
ctx.lineWidth = width;
ctx.beginPath();
ctx.moveTo(startX,startY);
ctx.lineTo(endX,endY);
ctx.stroke();
ctx.restore();
}
function drawBorder( x, y, width, height, lineWidth, strokeColor ) {
ctx.save();
ctx.lineWidth = lineWidth;
ctx.strokeStyle = strokeColor;
ctx.strokeRect( x, y, width, height);
ctx.restore();
}
function drawRect( x, y, width, height, fillColor ) {
ctx.save();
ctx.fillStyle = fillColor;
ctx.fillRect( x, y, width, height );
ctx.restore();
}
// ====================
// Global variables
// ====================
var ctx = $("#canvas")[0].getContext('2d'),
canvas = document.getElementById('canvas'),
cHeight = canvas.height = 500,
cWidth = canvas.width = 500,
canvasOffset = $('#canvas').offset(),
offsetX = canvasOffset.left,
offsetY = canvasOffset.top,
frameCounter = 0,
enemyList = [],
spawningEnemies_FLAG = true,
isLeftMouseButtonHeld_FLAG = false,
cursors = ['default', 'pointer'],
enemiesOnMap = 100,
fps = 120,
mouseX,
mouseY;
// canvas settings
ctx.font = '22px Arial';
var viewPort = {
x: cWidth/2,
y: cHeight/2,
update() {
this.x = cWidth / 2 - player.x;
this.y = cHeight / 2 - player.y;
}
};
var sharedBehaviour = {
x: cWidth / 2,
y: cHeight / 2,
id: undefined,
type: 'entity',
width: 15,
height: 15,
fillColor: '#E15258',
targetX: null,
targetY: null,
bulletSpeed: 1,
speed: 1,
update( type ) {
// if there is target
if( this.targetX !== null ) {
// Find out distance to target
var distanceX = this.targetX - this.x; // distance on X axis
var distanceY = this.targetY - this.y; // distance on Y axis
var distanceToTarget = Math.sqrt( distanceX*distanceX + distanceY*distanceY ); // distance
// If distance is smaller or equal speed, then just set position
if( distanceToTarget <= this.speed ) {
this.x = this.targetX;
this.y = this.targetY;
// Then reset
this.targetX = this.targetY = null;
} else { // If distance is bigger than speed, so we want to move with speed
distanceX = distanceX / distanceToTarget;
distanceY = distanceY / distanceToTarget;
distanceX = distanceX * this.speed;
distanceY = distanceY * this.speed;
this.x += distanceX;
this.y += distanceY;
}
}
},
draw( type ) {
drawRect( this.x - this.width/2, this.y - this.height/2, this.width, this.height, this.fillColor );
},
isColliding( entity ) {
return testCollisionRectRect( this, entity );
}
};
var player = Object.assign({}, sharedBehaviour, {
x: cWidth/2 - 12.5,
y: cHeight/2 - 12.5,
id: 980722,
type: 'player',
width: 25,
height: 25,
fillColor: '#82d877',
speed: 2.5,
isWithinRange( entity, val ) {
// Check if enemy is on player
var distanceX = this.x - entity.x;
var distanceY = this.y - entity.y;
return Math.sqrt( distanceX*distanceX + distanceY*distanceY ) < val;
}
});
function createEnemy( x, y, type, width, height, fillColor, speed, name ) {
var newEnemy = Object.assign({}, sharedBehaviour, {
x,
y,
type,
width,
height,
name,
fillColor,
speed,
setTarget( entity ) {
this.targetX = entity.x + randomNumberFromRange(-50, 50); // Set X with some random number
this.targetY = entity.y + randomNumberFromRange(-50, 50); // Set Y with some random number
},
isOnPlayer( val ) {
// Check if enemy is on player
var distanceX = player.x - this.x;
var distanceY = player.y - this.y;
return Math.sqrt( distanceX*distanceX + distanceY*distanceY ) < val;
},
isMouseOver( ) {
return (this.mouseX + this.width/2 >= this.x && this.mouseX + this.width/2 <= this.x + this.width && this.mouseY + this.height/2 >= this.y && this.mouseY + this.height/2 <= this.y + this.height);
}
});
enemyList.push( newEnemy );
}
function newGame( ) {
player.x = cWidth/2 - 12.5;
player.y = cHeight/2 - 12.5;
frameCounter = 0;
enemyList = [];
spawningEnemies_FLAG = true;
isLeftMouseButtonHeld_FLAG = false;
// Spawning enemies
for( i=0; i < randomNumberFromRange(15, 25); i++ ) {
createEnemy( randomNumberFromRange(0, cWidth), randomNumberFromRange(0, cHeight), 'passive', randomNumberFromRange(12, 18), randomNumberFromRange(12, 18), 'red', 1, 's' );
createEnemy( randomNumberFromRange(0, cWidth), randomNumberFromRange(0, cHeight), 'agressive', randomNumberFromRange(10, 16), randomNumberFromRange(10, 16), 'lightblue', 1.5, 'l' );
}
update();
}
function update( ) {
frameCounter++;
ctx.setTransform(1,0,0,1,0,0); // reset before clearing
ctx.clearRect(0,0,cWidth,cHeight);
// ========== Update ==========
// Player
player.update('player');
// Enemies
enemyList.forEach( enemy => { enemy.update(); });
// viewPort
viewPort.update()
// ========== Draw ==========
// viewPort
ctx.setTransform(1,0,0,1, viewPort.x, viewPort.y);
// Enemies
enemyList.forEach( enemy => { enemy.draw(); });
// Player
player.draw('player');
// ========== Conditions ==========
// Enemies
// Behaviour
enemyList.forEach( enemy => {
if ( Math.random() < ( 1 / 15 ) ) {
if ( enemy.isOnPlayer( player.circleRadius ) ) {
if ( ! (enemy.isOnPlayer( 50 )) ) {
enemy.setTarget( player );
}
}
}
if ( Math.random() < ( 1 / 800 )) {
if ( ! (enemy.isOnPlayer( player.circleRadius )) ) enemy.setTarget( enemy );
}
if ( enemy.isOnPlayer(player.circleRadius/2 - 25 ) ) {
enemy.targetX = enemy.targetY = null;
}
( enemyList.length === enemiesOnMap ) ? spawningEnemies_FLAG = false : spawningEnemies_FLAG = true;
});
// this is bad vvv
// setTimeout(function() {
requestAnimationFrame(update);
//}, 1000 / fps);
}
function setPlayerTarget_and_checkPlayerPosition( mouse ) {
player.targetX = mouseX;
player.targetY = mouseY;
// a few lines are gone here...
}
canvas.addEventListener('mousedown', function( mouse ) {
isLeftMouseButtonHeld_FLAG = true;
setPlayerTarget_and_checkPlayerPosition(mouse);
});
canvas.addEventListener('mouseup', function( mouse ) {
isLeftMouseButtonHeld_FLAG = false;
});
canvas.addEventListener('mousemove', function( mouse ) {
if( isLeftMouseButtonHeld_FLAG ) {
setPlayerTarget_and_checkPlayerPosition(mouse);
}
});
canvas.addEventListener('mousemove', function( mouse ) {
var rect = canvas.getBoundingClientRect(); // offset may change in snippets
mouseX = parseInt(mouse.clientX - rect.left - viewPort.x);
mouseY = parseInt(mouse.clientY - rect.top - viewPort.y);
});
newGame();
canvas { border: 1px solid black; background-color: white; }
<canvas id="canvas"></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
source to share