This question already has answers here:
JavaScript: Collision detection
(10 answers)
Closed 2 years ago.
What a good practice to detect collision of 2 objects(walls). Yes, not just detection, but further displacement so that objects do not enter each other. That is, so that when they collide, they rest against each other, but do not enter.
CODE
class WallObj {
constructor(obj) {//x, y, w, h, bern ,thru) {
this.x = obj.x
this.y = obj.y
this.w = obj.w
this.h = obj.h
this.bern = obj.bern
this.thru = obj.thru
this.hide = obj.hide
this.id = obj.id
}
collusionWall(startPosition, endPosition) {
var xS = startPosition[0]
var x = endPosition[0]
if (xS - x > 0)
if (x)
// if wall behind point
if (this.x < startPosition[0])
return endPosition
else if (this.x + this.w < x)
return endPosition
return endPosition
// return [this.x, endPosition[1]]
}
}
a naive approach would be to check x and y :
let ctx = mycan.getContext("2d");
let objects = [];
class player
{
constructor()
{
this.position = {x:50,y:50};
this.color = "blue";
this.size = 32;
this.stop= false;
this.prevpos=this.position;
window.addEventListener("keydown",(e)=>{
if(this.stop) this.position=this.prevpos;
else this.displacement(e);
});
}
displacement(e)
{
this.prevpos = this.position;
this.position.x+=(e.key=="ArrowRight");
this.position.x-=(e.key=="ArrowLeft");
this.position.y+=(e.key=="ArrowDown");
this.position.y-=(e.key=="ArrowUp");
}
draw()
{
ctx.fillStyle = this.color; ctx.fillRect(this.position.x,this.position.y,this.size,this.size);
}
};
class wall
{
constructor(posx,posy)
{
this.position = {x:posx,y:posy};
this.color = "red";
this.size = 32;
}
draw(){
ctx.fillStyle = this.color;
ctx.fillRect(this.position.x,this.position.y,this.size,this.size);
}
};
for(let i = 0; i<mycan.width;i+=32)
{
objects.push(new wall(i,0));
objects.push(new wall(i,mycan.height-32));
}
for(let j = 0; j<mycan.height;j+=32)
{
objects.push(new wall(0,j));
objects.push(new wall(mycan.width-32,j));
}
let playr=new player;
let collision = (colider)=>{
let colx = false;
let coly = false;
/*******************************************************
here we check if the top left point from our current
wall object is inferior to the top left point of our
player and if the top rignt point of the wall object is
superior to the player top left point.
we need to repeat this for the player top right point
(so we compare the player top right point is superior
to the wall top left point and inferior to the wall
top right point)
then we repeat this for y
*******************************************************/
for(let object of objects)
{
colx = (
(
(object.position.x<=colider.position.x) &&
(
(object.position.x+object.size)>=
(colider.position.x)
)
)||(
(
(colider.position.x+colider.size)>=object.position.x) &&
(
(colider.position.x+object.size)<=(object.position.x+object.size)
)
)
)
coly = (
(
(object.position.y<=colider.position.y) &&
(
(object.position.y+object.size)>=
(colider.position.y)
)
)||(
(
(colider.position.y+colider.size)>=object.position.y) &&
(
(colider.position.y+object.size)<=(object.position.y+object.size)
)
)
)
if(colx&&coly) return true;
}
return false;
};
setInterval(()=>{
ctx.clearRect(0,0,mycan.width,mycan.height);
playr.stop = collision(playr);
playr.draw();
for(let obj of objects)
obj.draw();
},1000/30);
<canvas id="mycan" width=400 height=250></canvas>
à better approach would be to cut your 2d world into zones where the density of objects that can collide is more or less important (a quadtree).
like so :
and an easier way would be to look if the colided object is in a sphere (this would mean that walls have a spheric collision and could hardly be represented as a square) but we could just say that the player is a sphere and check if something has entered his radius.
https://studiofreya.com/3d-math-and-physics/sphere-vs-aabb-collision-detection-test/
Related
I'm making a small game with one player and blocks that builds up the environment. The problem I'm having is knowing the difference between when the player hits the ground (the top of a block), and hitting a wall (the side of the block).
So far the player can walk on the ground just fine, but when he meets a wall, he immediately jumps to the top of that block.
This is my collision detector:
function collisionDetector(){
if(myPlayer.y + myPlayer.h > c.height){ //Bottom of the canvas
myPlayer.vy = 0;
myPlayer.ay = 0;
myPlayer.y = c.height - myPlayer.h;
myPlayer.onGround = true;
console.log(myPlayer.y + myPlayer.h, c.height);
}
if(myPlayer.x + myPlayer.w >= c.width){ //right side of canvas
myPlayer.x = c.width - myPlayer.w;
myPlayer.vx = 0;
}
if(myPlayer.x <= 0){ //Left side of canvas
myPlayer.x = 0;
myPlayer.vx = 0;
}
function hitTest(a,b){ //hitTest between two objects
if(a.y + a.h > b.y && a.y < b.y + b.h && a.x + a.w > b.x && a.x < b.x + b.w){
return true;
}
}
for(var i = 0; i < blocks.length; i++){ //Loop through blocks
if(hitTest(myPlayer, blocks[i])){ //If it touches a block
myPlayer.y = blocks[i].y - myPlayer.h;
myPlayer.onGround = true; //onGround = ready to jump
}
}
}
I realized that I'm setting the players y pos to be on top of what ever block it hits, but I cannot figure out a solution to this problem. Can anyone help me or at least lead me in the right direction? Thanks!
(Let me know if you need more of the code)
PS: the player is just a head. No body hiding behind the blocks.
So basically, what you need to do is to check collision between many points in the player.
In the snippet you can show many points represented in the player.
Bottom almost-left and almost-right (in blue), check against below blocks. They are not fully left or right, in order to prevent a race condition which will allow the player to climb walls. In that case, if the player is pushing against a wall and jumping, the collider will detect both side collision and bottom collision as true, then the player will quickly move to the top until there are no more blocks.
Left and right points (in black), check against edges of the blocks. This is just a point instead of two like the bottom edge, because we don't need more for this particular case. One more for each side could be easily added to get a better detection.
Top point (in red) checks against the top blocks. This is in the middle in order to allow the player a more easy way to tranverse the map. If this is not needed, you would need to add one more point like in the bottom edge (but never reaching the far edge, because that will generate a race condition).
So in summary, to have a good collision detection based on points (instead of raycasts), you need to detect the player like if it where a rounded shape, in order to prevent strange behaviours.
You can player around with the map layout by altering the layout variable. 0's are empty space, 1's are brown blocks and 2's are green blocks.
The collisionDetector fuction has comments to understand what's going on.
Also I have added a jump feature since I understand you would need that as well.
const c = document.getElementById('canvas');
c.width = window.innerWidth;
c.height = window.innerHeight;
const ctx = c.getContext('2d');
// map layout
const layout =
`000000001
001000001
000000101
100110111
222222222`;
// convert layout to blocks
const blocks = [...layout].reduce((a, c, i) => {
if (i === 0 || c === "\n") a.push([]);
if (c === "\n") return a;
const y = a.length - 1;
const row = a[y];
const x = row.length;
row.push({x: x * 32, y: y * 32, t:c, w:32, h:32});
return a;
}, []).reduce((a, c) => a.concat(c), []);
// player starting position
const myPlayer = {x: 32*1.5, y: 0, h: 32, w: 16, onGround: true};
const gravity = -1;
let pkl = 0, pkr = 0;
let pvely = 0;
function render() {
// player logic
const pvelx = pkr + pkl;
const speed = 2;
myPlayer.x += pvelx * speed;
myPlayer.y -= pvely;
if (pvely > -2) pvely += gravity;
const debugColliders = collisionDetector();
ctx.clearRect(0, 0, c.width, c.height);
// player render
ctx.fillStyle = '#FFD9B3';
ctx.fillRect(myPlayer.x, myPlayer.y, myPlayer.w, myPlayer.h);
renderLayout();
debugColliders();
window.requestAnimationFrame(render);
}
function renderLayout() {
const colors = {'1': '#A3825F', '2': '#7FAC72'}
blocks.forEach(b => {
if (+b.t > 0) {
ctx.fillStyle = colors[b.t];
ctx.fillRect(b.x, b.y, b.w, b.h);
}
});
}
window.addEventListener('keydown', e => {
if (e.key == 'ArrowRight') {
pkr = 1;
e.preventDefault();
} else if (e.key == 'ArrowLeft') {
pkl = -1;
e.preventDefault();
} else if (e.key == 'ArrowUp') {
if (myPlayer.onGround)
pvely = 8;
myPlayer.onGround = false;
e.preventDefault();
}
});
window.addEventListener('keyup', e => {
if (e.key == 'ArrowRight') {
pkr = 0;
} else if (e.key == 'ArrowLeft') {
pkl = 0;
}
});
function collisionDetector(){
const p = myPlayer;
const playerTop = p.y;
const playerLeft = p.x;
const playerRight = playerLeft + p.w;
const playerBottom = playerTop + p.h;
const playerHalfLeft = playerLeft + p.w * .25;
const playerHalfRight = playerLeft + p.w * .75;
const playerHMiddle = playerLeft + p.w * .5;
const playerVMiddle = playerTop + p.h * .5;
if(playerBottom > c.height){ //Bottom of the canvas
p.vy = 0;
p.ay = 0;
p.y = c.height - p.h;
p.onGround = true;
}
if(playerRight >= c.width){ //right side of canvas
p.x = c.width - p.w;
p.vx = 0;
}
if(playerLeft <= 0){ //Left side of canvas
p.x = 0;
p.vx = 0;
}
blocks.forEach(b => { //Loop through blocks
if (b.t === "0") return; // If not collidable, do nothing
const blockTop = b.y;
const blockLeft = b.x;
const blockRight = blockLeft + b.w;
const blockBottom = b.y + b.h;
// Player bottom against block top
if ((playerBottom > blockTop && playerBottom < blockBottom) && // If player bottom is going through block top but is above block bottom.
((playerHalfLeft > blockLeft && playerHalfLeft < blockRight) || // If player left is inside block horizontal bounds
(playerHalfRight > blockLeft && playerHalfRight < blockRight))) { // Or if player right is inside block horizontal bounds
p.y = blockTop - p.h;
p.onGround = true;
}
// Player top against block bottom
if ((playerTop < blockBottom && playerTop > blockTop) && // If player top is going through block bottom but is below block top.
((playerHMiddle > blockLeft && playerHMiddle < blockRight))) { // If player hmiddle is inside block horizontal bounds
p.y = blockBottom;
p.onGround = false;
}
// Player right against block left, or player left against block right
if (playerVMiddle > blockTop && playerVMiddle < blockBottom) { // If player vertical-middle is inside block vertical bounds
if ((playerRight > blockLeft && playerRight < blockRight)) { // If player vmiddle-right goes through block-left
p.x = blockLeft - p.w;
} else if ((playerLeft < blockRight && playerRight > blockLeft)) { // If player vmiddle-left goes through block-right
p.x = blockRight;
}
}
});
return function debug() {
ctx.fillStyle = 'black';
ctx.fillRect(playerLeft, playerVMiddle, 1, 1);
ctx.fillRect(playerRight, playerVMiddle, 1, 1);
ctx.fillStyle = 'red';
ctx.fillRect(playerHMiddle, playerTop, 1, 1);
ctx.fillStyle = 'blue';
ctx.fillRect(playerHalfLeft, playerBottom, 1, 1);
ctx.fillRect(playerHalfRight, playerBottom, 1, 1);
}
}
window.requestAnimationFrame(render);
html, body{ width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden; }
canvas { background: #7AC9F9; display: block; }
<canvas id="canvas"></canvas>
Introduce a block[i].type attribute. For instance if block[i].type=='floor' then make player stay on floor. If for another instance block[i].type=='wall' then make it stop moving through the wall. When block[i].type=='brick' or square or block or whatever, them it's a mixture of two.
Another part to be edited is when you check the collisions. What if you have only one-direction collision? What I am saying is maybe use or instead of and in this part if(a.y + a.h > b.y && a.y < b.y + b.h && a.x + a.w > b.x && a.x < b.x + b.w){
Also you could check each collision separately, like
function hitTest(a,b){ //hitTest between two objects
var collisions = {up: false, down: false, left: false, right: false};
collisions.up = (a.y + a.h > b.y ) || collisions.up
collisions.down = (a.y < b.y + b.h ) ||collisions.down
collisions.right = ( a.x + a.w > b.x) || collisions.right
collisions.left = (a.x < b.x + b.w) || collisions.left
return collisions
}
var escapeFrom = {
down: function(player, block){
player.y = block.y + block.h;
player.onGround = true; //onGround = ready to jump
},
up: function(player, block){
// you logic to escape from hitting the ceiling
},
// and for the next 2
left: function(player, block) {},
right: function(player, block){}
}
// Now here you check whether your player hits blocks
for(var i = 0; i < blocks.length; i++){ //Loop through blocks
cls = hitTest(myPlayer, blocks[i]) //If it touches a block
Object.keys(cls).map(function(direction, ind){
if (cls[direction]){
// call escape from function to escape collision
escapeFrom[direction](myPlayer, blocks[i]);
}
})
}
This is highly unoptimized, the whole your code is unoptimized, but at least it can help to move further.
Ok, so I wrote some code for a JS game. The code itself works, but isn't in proper OOP form. In the class "Enemy" I need to reference variables and a method from the "Player" class. Look in the "Collision" method, where the variables are referenced. Notice that I get the data specifically from the new instance of "Player" called "player" at the end of the script. For OOP, how am I suppose to share information between these two classes?
Thanks!
var Player = function() {
this.x = 15;
this.y = 15;
};
Player.prototype.reset = function() {
this.x = 200; // reset to this
this.y = 320; // reset to this
};
var Enemy = function() {
this.x = 25;
this.y = 30;
};
Enemy.prototype.collision = function() {
if (player.x >= this.x - 35 & player.x <= this.x + 35) { // check column
if (player.y >= this.y - 30 & player.y <= this.y + 30) { // check row
player.reset(); // calls player method "reset"
}
}
};
// Start Game
setEnemies();
var player = new Player();
in javascript functions can take arguments
solution to your problem could be passing instance of Player to method collision
as #Álvaro Touzón mentioned, a good practice would be to use inheritance as Enemy and Player in your code are now basically the same
also, you could read about ES6 classes, which make programming a bit easier, however they still rely on prototype inheritance which makes them just a syntactic sugar
If you want to use OOP, then perhaps this will help you.
Add helper extend function
function extend(current, base) {
for (var key in base) {
if (base.hasOwnProperty(key)) {
current[key] = base[key];
}
}
current.prototype = Object.create(base.prototype);
};
Create class Avatar as #Álvaro Touzón suggested
var Avatar = (function () {
function Avatar (x, y) {
this.x = x;
this.y = y;
}
Avatar.prototype.reset = function() {
return this;
};
Avatar.prototype.collision = function(object) {
if (object.x >= this.x - 35 & object.x <= this.x + 35) { // check column
if (object.y >= this.y - 30 & object.y <= this.y + 30) { // check row
object.reset(); // calls object method "reset"
}
}
};
return Avatar;
}());
Class Player and Enemy
var Player = (function (superClass) {
extend(Player, superClass);
function Player (x, y) {
superClass.apply(this, arguments);
}
Player.prototype.reset = function() {
this.x = 200; // reset to this
this.y = 320; // reset to this
return this;
};
return Player;
}(Avatar));
var Enemy = (function (superClass) {
extend(Enemy, superClass);
function Enemy (x, y) {
superClass.apply(this, arguments);
}
return Enemy;
}(Avatar));
Create Game
var Game = (function () {
Game.prototype.player = new Player(15, 15);
Game.prototype.enemys = [
new Enemy(25, 30),
new Enemy(10, 30),
];
function Game () {
// setup
}
Game.prototype.start = function() {
for (var i = 0; i < array.length; i++){
var enemy = this.enemys[i];
this.player.collision(enemy);
}
return this;
};
return Game;
}());
Use:
var game = new Game();
game.start();
How it works
You have a group of objects - enemies and the object - player, all of them have the ability to calculate collision between each other, because common ancestor. Every time you call the start of the game will be calculated collision. I would add setInterval in game.start for calculated collision but this complicate the code.
I cannot say how you want to run your game but in general, to share data I always believe in making singleton classes and use it in different objects. This way we can do better error handling also. In js, there are no singleton classes as such to say. But you can always make simple js modules like below:
var abc = (function(){
var abc = "someval"; //private variable
var setAbc = function() {
//some logic
}
var getAbc = function() {
//some logic
}
var publicMethod = function {
//some logic
}
return {
setAbc: setAbc,
getAbc: getAbc,
publicMethod: publicMethod
}
})();
It's non of the buisness of the Enemy, to mutate/reset the Player. Nor is it the buisness of the Player, to wich Position it is reset to.
These things should be done by the game in the main game loop, and the collision-method should only determine wether this Element has hit the passed Element.
//manages the bounding-Box / Collisions,
//also pretty much everything related to (basic) rendering, like Assets/Image or DOM-Node, ... but since you've not included that in your code, I can't adapt it.
class Element{
constructor(config){
let {x, y, width, height, hitBoxOffsetX, hitBoxOffsetY} = config || {};
this.x = +x || 0;
this.y = +y || 0;
this._hitBoxOffsetX = +hitBoxOffsetX || 0;
this._hitBoxOffsetY = +hitBoxOffsetY || 0;
this.width = +width || 0;
this.height = +height || 0;
}
//bounding box
get left(){ return this.x + this._hitBoxOffsetX }
get right(){ return this.left + this.width }
get top(){ return this.y + this._hitBoxOffsetY }
get bottom(){ return this.top - this.height }
collision(other){
return this.right > other.left && this.left < other.right &&
this.top > other.bottom && this.bottom < other.top;
}
}
//everything that can somehow be hit, extends Element
class Player extends Element {
constructor(){
super({
hitBoxOffsetX: -35, //hitBox starts 35px to the left of this.x
hitBoxOffsetY: -30, //hitBox starts 30px to the top of this.y
width: 70, //width of the hitBox
height: 60 //height of the hitBox
});
}
}
class Enemy extends Element {
constructor(){
//since I have no idea about the dimensions of these Entities
super();
}
}
//and walls, for example
class Wall extends Element {
constructor(x, y, width, height){
super({
x, y,
width: +width || 20,
height: +height || 20
});
}
}
And the mentioned collision-checking and resetting happens in the main game-loop, but since I don't know how your game-loop looks like, here some pseudocode:
update(){
requestAnimationFrame(update);
//move everything
//collision-checks
//if your player has some sort of weapon, maybe you want to first check
//wether it has "killed" some enemies,
//before checking wether an enemy has hit your player.
var hitEnemy = enemies.find(enemy => enemy.collision(player));
//btw. Bullets would also be "enemies"
if(hitEnemy){
//and you probably don't want to
player.x = currentLevel.playerResetPosition.x;
player.y = currentLevel.playerResetPosition.y;
//destroy hitEnemy
//maybe mark the player as blinking/non-hitable for a few seconds?
}
//render everything
}
it's my first question.
I'm doing an Asteroids game copy and I've started with spaceship movement, but
I've got a problem with rotating it in canvas in js. The problem is how to stop rotation after pressing key? It's rotating in two directions, but when I release the key, object returns to its initial state.
here is code:
window.addEventListener('keydown',doKeyDown,true);
window.addEventListener('keyup',doKeyUp,true);
//var x = canvas.width/2;
//var y = canvas.height/2;
var keys = new Array();
function doKeyDown(evt){
keys[evt.keyCode] = true;
}
function doKeyUp(evt){
keys[evt.keyCode] = false;
}
var context = document.getElementById('pageCanvas').getContext('2d');
var angle = 0;
var angle2 = 0;
function convertToRadians(degree) {
return degree*(Math.PI/180);
}
function incrementAngle() {
angle -= 10;
if(angle > 360) {
angle = 0;
}
}
function decrementAngle(){
angle2 += 10;
if(angle2>360){
angle2 = 0;
}
}
function drawRandomlyColoredRectangle() {
context.save();
context.clearRect(0,0,500,500);
fillStyle = "#000000";
context.fillRect(0,0,500,500);
incrementAngle();
decrementAngle();
context.translate(200,200);
if(37 in keys && keys[37]){
context.rotate(convertToRadians(angle));
console.log("lewo");
};
if (39 in keys && keys[39]){ //right
//x += dx/5;
//rotacja w prawo
context.rotate(convertToRadians(angle2));
console.log("prawo");
};
context.fillStyle = "green";
context.fillRect(-25,-25,50,50);
context.restore();
}
setInterval(drawRandomlyColoredRectangle, 20);
and fiddle http://jsfiddle.net/tomasthall/covyjaLb/2/
Help, please :c
Just store the current value of angle in the variable and use it in each rendering frame.
if(37 in keys && keys[37]){
decrementAngle();
};
if (39 in keys && keys[39]){
incrementAngle();
};
(...)
context.rotate(convertToRadians(angle));
Check and study other fixes and read some tutorials/articles before asking next questions...
http://jsfiddle.net/covyjaLb/3/
I'm trying my hands at some simple game programming in Javascript and have come to realize I need to change the way I handle sprites. The only question is, "how"?
I have a hero that moves around with the arrow keys and fires laser rays with WASD. This is how I define rays:
function Ray (x, y, width, height, direction, index) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.direction = direction;
this.index = index;
this.speed = 512;
this.disabled = false;
}
The index just indicates where in an array of rays (heh) it is being stored. I currently have a hard-coded limit of 5 simultaneous rays, although the other restrictions (screen size, ray size, speed, hero size etc) shouldn't allow for more than 4:
var rays = [];
var numberOfRays = 0;
var rayLimit = 5;
var shotClock = 300;
And so, in the update() function that gets called by the game loop, I have listeners for the WASD keys. They look like this:
// D
if (68 in keysDown && numberOfRays <= rayLimit && Date.now() - lastShootTime > shotClock) {
lastShootTime = Date.now();
var newRayIndex = findFreeRay();
rays[newRayIndex] = new Ray(hero.x + hero.width + 12, hero.y + hero.height / 2, rayImage.width, rayImage.height, 'right', newRayIndex);
numberOfRays++;
}
(findFreeRay() just returns the lowest unused or disabled (off the screen) index in rays[])
Earlier in the update() method (I have also tried putting it later) I have the logic for updating ray movement:
rays.forEach(function(ray) {
if (ray != null && !ray.disabled) {
switch(ray.direction) {
case 'right':
ray.x += ray.speed * modifier;
break;
case 'left':
ray.x -= ray.speed * modifier;
break;
case 'up':
ray.y -= ray.speed * modifier;
break;
case 'down':
ray.y += ray.speed * modifier;
break;
}
}
});
Finally, there is the image for the ray (actually, one for horizontal rays and another one for vertical rays). Currently, I am using one Image object of each globally, that the existing rays share. But I have also tried, without much luck, to create individual image objects for every ray.
// Ray images
var rayReady = false;
var rayImage = new Image();
rayImage.onload = function() {
rayReady = true;
};
rayImage.src = "images/ray.png";
var rayVertReady = false;
var rayVertImage = new Image();
rayVertImage.onload = function() {
rayVertReady = true;
};
rayVertImage.src = "images/ray_vert.png";
And here is how they get drawn:
if (rayReady && rayVertReady && numberOfRays > 0) {
rays.forEach(function(ray) {
if (ray.x > canvas.width
|| ray.x + ray.width < 0
|| ray.y > canvas.height
|| ray.y + ray.height < 0) {
numberOfRays--;
ray.disabled = true;
}
else if (ray.direction == 'right' || ray.direction == 'left'){
ctx.drawImage(rayImage, ray.x, ray.y);
}
else {
ctx.drawImage(rayVertImage, ray.x, ray.y);
}
});
}
The problem
After shooting only a few rays, new ones start to either flicker and disappear, or stay invisible altogether. They actually exist as gameplay objects though, as they can hit targets.
What likely causes this flickering?
(credit to Matt Hackett for the base of this code)
fiddle: http://jsfiddle.net/Vr3MW/
I'm writing a simple game in javascript and I'm wondering what the best way to handle collisions between the player and the world objects.
<script>
var isJumping = false;
var isFalling = false;
var w = 1;
var recwidth = 400;
var recheight = 400;
var xpos = 50;
var ypos = 279;
window.onload = function() {
var FPS = 30;
var ground = new myObject();
setInterval(function() {
clear();
draw();
ground.draw(0, 325);
ground.draw(125,325)
}, 1000/FPS);
};
function myObject(){
this.draw = function drawground(groundx, groundy){
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
//context.fillRect(xpos,ypos,100,100);
var img=new Image()
img.src="ground.png"
img.onload = function() {
context.drawImage(img,groundx,groundy)}
}
};
function jump()
{
var t=.1;
isJumping=true;
var jumpint= setInterval(function() {
yup = 12*t-(5*t*t);
ypos= ypos - yup;
t = t + .1
if(yup < 0)
{
isJumping = false;
isFalling = true;
clearInterval(jumpint);
jumpint = 0;
fall();
return;
}
}, 20);
}
function fall()
{
t=.10
var fallint= setInterval(function() {
ydown = (5*t*t);
ypos= ypos + ydown;
t = t + .1
if(ypos > 275)
{
isFalling == false;
clearInterval(fallint);
fallint = 0;
return;
}
}, 20);
}
function changex(x){
xpos = xpos + (x);
//clear();
//draw();
}
function changey(y){
ypos = ypos + (y);
//clear();
//draw();
}
function draw(){
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
var img=new Image()
img.src="character.png"
img.onload = function() {
context.drawImage(img,xpos,ypos)}
}
function clear(){
var canvas = document.getElementById('canvas')
var context = canvas.getContext('2d');
context.clearRect(0,0, canvas.width, canvas.height);
}
document.onkeydown = function(event) {
var keyCode;
if(event == null)
{
keyCode = window.event.keyCode;
}
else
{
keyCode = event.keyCode;
}
switch(keyCode)
{
// left
case 37:
//left
changex(-5);
break;
// up
case 38:
// action when pressing up key
jump();
break;
// right
case 39:
// action when pressing right key
changex(5);
break;
// down
case 40:
// action when pressing down key
changey(5);
break;
default:
break;
}
}
</script>
So, as you can see I'm creating two objects so far, and the player stops falling at any arbitrary point. I feel collisions at this stage wont be too difficult, but once I start adding more I feel it's going to get more difficult. I'm not going to be using the instance of the object with the same image for each instance of the object, so at some point I'm going to change the myobject function to be able to accept the image as a parameter, and then checking for collisions will be a bit more tricky. I also plan on making this into a side scroller, so once one end the map is hit it changes into the next area, which is going to cause performance issues. If I'm checking for collisions on every single object in the entire game every interval I imagine things are going to get slow. What is going to be the best way to limit the number of collisions checked? Obviously, if the object isn't on screen there is no need to check it, but is there a way to limit that. I'm thinking of making an array for every frame of the game, and filling that array with it's objects. Then, only check the array the of the frame the player is currently in. Is this feasible or still going to cause too many issues? Any help is greatly appreciated.
If you want pixel perfect collisions, I have some plain javascript code that worked for me with canvas2d rendering context.
function collide(sprite, sprite2, minOpacity=1) {
// Rectangular bounding box collision
if (sprite.x < sprite2.x + sprite2.width && sprite.x + sprite.width > sprite2.x && sprite.y < sprite2.y + sprite2.height && sprite.y + sprite.height > sprite2.y) {
// Finds the x and width of the overlapping area
var overlapX = (this.rect.x > other.rect.x) ? [this.rect.x, (other.rect.x + other.rect.width) - this.rect.x + 1] : [other.rect.x, (this.rect.x + this.rect.width) - other.rect.x + 1];
// Finds the y and height of the overlapping area
var overlapY = (this.rect.y + this.rect.height > other.rect.y + other.rect.height) ? [this.rect.y, (other.rect.y + other.rect.height) - this.rect.y + 1] : [other.rect.y, (this.rect.y + this.rect.height) - other.rect.y + 1];
// Creates a canvas to draw sprite.image to
var spriteImageCanvas = new OffscreenCanvas(overlapX[0] + overlapX[1], overlapY[0] + overlapY[1]);
var spriteImageCanvasContext = spriteImageCanvas.getContext("2d");
// Draws sprite.image to spriteImageCanvasContext
spriteImageCanvasContext.drawImage(this.image, sprite.x, sprite.y, sprite.width, sprite.height);
// Creates a canvas to draw sprite2.image to
var sprite2ImageCanvas = new OffscreenCanvas(overlapX[0] + overlapX[1], overlapY[0] + overlapY[1]);
var sprite2ImageCanvasContext = otherImageCanvas.getContext("2d");
// Draws sprite2.image to sprite2ImageCanvasContext
sprite2ImageCanvasContext.drawImage(sprite2.image, sprite2.x, sprite2.y, sprite2.width, sprite2.height);
// Loops through the x coordinates in the overlapping area
for (var x = overlapX[0]; x <= overlapX[0] + overlapX[1]; x++) {
// Loops through the y coordinates in the overlapping area
for (var y = overlapY[0]; y <= overlapY[0] + overlapY[1]; y++) {
if (/* Checks if the pixel at [x, y] in the sprite image has an opacity over minOpacity input */ thisImageCanvasContext.getImageData(x, y, 1, 1).data[3] >= minOpacity && /* Checks if the pixel at [x, y] in the sprite2 image has an opacity over minOpacity input */ otherImageCanvasContext.getImageData(x, y, 1, 1).data[3] >= minOpacity) {
return true;
};
};
};
};
}
Or if you just want rectangular collision, use the first if statement in the function.