Recursion loop to check if conditions good, or try again - javascript

I have to generate several objects on a canvas.
The problem is that if my conditions (IF statements) are working, it is not looping again if the conditions are not met.
So I sometimes have only 1 player instead of 2, etc.
The function which sets the piece is setPiece(), with different conditions depending of the objects (obstacle, weapon, player).
Each object has in own function which call setPiece: setObstacles, setWeapons, setPlayers.
I have already tried to call these functions after an "else", so setPiece loop again. But it causes a stack error in the console, too many loops.
I also tried a While loop but don't succeded to implement it, so it is also an infinite loop.
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-compatible" content="IE-edge">
<meta name="viewport" content="width=device-width, initial-scale-1">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.0/animate.css">
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
<title>Lava Temple</title>
<style>
* {
padding: 0;
border: 0;
}
body{
background-color: #181818;
}
#board {
display: block;
background-color: white;
margin: 0 auto;
margin-top: 100px;
}
</style>
</head>
<body>
<canvas id="board" width="800" height="800"></canvas>
<script>
// BOARD OBJECT
function Board(width, height) {
this.width = width;
this.height = height;
this.chartBoard = [];
// Création du plateau logique
for (var i = 0; i < this.width; i++) {
const row = [];
this.chartBoard.push(row);
for (var j = 0; j < this.height; j++) {
const col = {};
row.push(col);
}
}
}
let board = new Board(10, 10);
console.log(board);
const ctx = $('#board').get(0).getContext('2d');
Board.prototype.drawBoard = function () {
for (var i = 0; i < this.width; i++) {
for (var j = 0; j < this.height; j++) {
ctx.beginPath();
ctx.strokeStyle = 'black';
ctx.strokeRect(j * 64, i * 64, 64, 64);
ctx.closePath();
}
}
};
board.drawBoard();
// OBJECTS OF THE GAME
function Obstacle(name, sprite) {
this.name = name;
this.sprite = sprite;
}
const lava = new Obstacle("Lave", "assets/lave.png");
const lava1 = new Obstacle("Lave1", "assets/lave.png");
const lava2 = new Obstacle("Lave2", "assets/lave.png");
const lava3 = new Obstacle("Lave3", "assets/lave.png");
const lava4 = new Obstacle("Lave4", "assets/lave.png");
const lava5 = new Obstacle("Lave5", "assets/lave.png");
const lava6 = new Obstacle("Lave6", "assets/lave.png");
const lava7 = new Obstacle("Lave7", "assets/lave.png");
const lava8 = new Obstacle("Lave8", "assets/lave.png");
const lava9 = new Obstacle("Lave9", "assets/lave.png");
const lavaArray = [lava, lava1, lava2, lava3, lava4, lava5, lava6, lava7, lava8, lava9];
function Weapon(name, sprite, damage) {
this.name = name;
this.sprite = sprite;
this.damage = damage;
}
const dagger = new Weapon("Dague", "assets/dague.png", 5);
const sword = new Weapon("Epée", "assets/epee.png", 10);
const axe = new Weapon("Hache", "assets/hache.png", 15);
const flail = new Weapon("Fléau", "assets/fleau.png", 20);
const weaponArray = [dagger, sword, axe, flail];
function Player(name, sprite, life) {
this.name = name;
this.sprite = sprite;
this.life = life;
}
const player1 = new Player("Joueur 1", "assets/joueur1.png", 100);
const player2 = new Player("Joueur 2", "assets/joueur2.png", 100);
const playerArray = [player1, player2];
// INIT OF THE GAME (code about my question)
Board.prototype.setPiece = function (piece) {
let randomX = Math.floor(Math.random() * board.width);
let randomY = Math.floor(Math.random() * board.height);
let drawX = randomX * 64;
let drawY = randomY * 64;
if (randomX >= this.width || randomY >= this.height) {
throw new Error('Pièce hors limite');
}
if (piece instanceof Obstacle) {
if (!(this.chartBoard[randomY][randomX] instanceof Obstacle)) {
this.chartBoard[randomY][randomX] = piece;
ctx.fillRect(drawX, drawY,64,64);
}
} else if (piece instanceof Weapon) {
if (!(this.chartBoard[randomY][randomX] instanceof Obstacle) && (!(this.chartBoard[randomY][randomX] instanceof Weapon))) {
this.chartBoard[randomY][randomX] = piece;
ctx.fillStyle = "red";
ctx.fillRect(drawX, drawY,64,64);
}
} else if (piece instanceof Player) {
if (!(this.chartBoard[randomY][randomX] instanceof Obstacle) &&
(!(this.chartBoard[randomY][randomX] instanceof Weapon) &&
(!(this.chartBoard[randomY][randomX] instanceof Player) &&
((!(this.chartBoard[randomY][randomX + 1] instanceof Player)) || (typeof this.chartBoard[randomY][randomX + 1] === undefined)) &&
((!(this.chartBoard[randomY][randomX - 1] instanceof Player)) || (typeof this.chartBoard[randomY][randomX - 1] === undefined)) &&
((!(this.chartBoard[randomY + 1][randomX] instanceof Player)) || (typeof this.chartBoard[randomY + 1][randomX] === undefined)) &&
((!(this.chartBoard[randomY - 1][randomX] instanceof Player)) || (typeof this.chartBoard[randomY - 1][randomX] === undefined))))) {
this.chartBoard[randomY][randomX] = piece;
ctx.fillStyle = "blue";
ctx.fillRect(drawX, drawY,64,64);
}
} else {
throw new Error('Pièce non valide');
}
};
Board.prototype.setObstacles = function () {
for (let lava of lavaArray) {
const obstacle = board.setPiece(lava);
}
};
board.setObstacles();
Board.prototype.setWeapons = function () {
let numWeapons = 4;
let randomWeapon;
let spawnWeapon;
for (let i = 0; i < numWeapons; i++) {
randomWeapon = Math.floor(Math.random() * weaponArray.length);
spawnWeapon = board.setPiece(weaponArray[randomWeapon]);
}
};
board.setWeapons();
Board.prototype.setPlayers = function () {
for (let player of playerArray) {
const spawnPlayer = board.setPiece(player);
}
};
board.setPlayers();
</script>
</body>
</html>
Actual results: Sometimes everything works well, but there are 2 problems:
- A piece might not be set on the board (canvas).
- There is an error with the player object because the IF condition (random + or - x / y) try to check outside of the length of the board, so I get an error Undefined. Don't know how to resolve it.
Expected: Understand how to generate a dynamic generation of objects on this canvas, and to repeat a loop with multiple conditions. Also learn how to implement the limits of the canvas so there is no Undefined error.

I think the problem is with going out of boundaries with on of those randomY increments and decrements. If you console.log(randomY) right after else if (piece instanceof Player) { ... then it seems that you get only one player in those scenarios only when randomY is either 0 or 9.
You can try doing something along these lines right after else if (piece instanceof Player) { ... and this should resolve the issue.
if (randomY === 0){
randomY = randomY+1
}
if (randomY === 9){
randomY = randomY-1
}
I'm not sure if this would still be considered "random". Tiles would still be able to take edges though, so mechanics still feel "random".

Related

When spawning many iterations of a class object, how do I take an object out of the array using a if statement inside of the Class?

I'm a beginner using p5js and I'm trying to work with classes. I'm making a game where you have to find and click a 'wanted man', from a crowd.
So basically, a randomizer picks between 7 different types of 'civilians', and it's supposed to remove one of the types from the 'civilians' that have been spawned. After removing the 'wanted man', I want to add one wanted man so that there is only one 'wanted man'.
So the code spawns a bunch of random 'civilians', then it will delete all 'wanted man' types in the array, and add only one of them. I think there is a better way to do this though.
My basic desire is to have a crowd of 'civilians' that run around, - one of which is a 'wanted man' - and you would have to find and click that 'wanted man' (kind of like a hunting/assassination game).
This is the code for the sketch.js file:
var civilians = [];
var page = 0;
var man1img;
var man2img;
var man3img;
var man4img;
var man5img;
var man6img;
var aliemanimg;
var w;
var h;
var spawnCount = 14;
var wantedMan;
var randCiv;
function preload() {
man1img = loadImage("man1.png");
man2img = loadImage("man2.png");
man3img = loadImage("man3.png");
man4img = loadImage("man4.png");
man5img = loadImage("man5.png");
man6img = loadImage("man6.png");
aliemanimg = loadImage("alieman.png");
}
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
}
function setup() {
createCanvas(windowWidth, windowHeight);
imageMode(CENTER);
// wantedMan = round(random(0, 6));
wantedMan = 0;
for (var i = 0; i < spawnCount; i++) {
randCiv = round(random(0, 6));
w = random(windowWidth);
h = random(windowHeight);
civilians.push(new Civilian(w, h, wantedMan, randCiv));
console.log(wantedMan);
if (civilians[i].isWantedMan()) {
//OVER HERE \/
civilians.splice(i, 1);
}
}
civilians.push(new Civilian(w, h, wantedMan, wantedMan));
}
// page setup
// page 1 : main screen (play, settings, and those stuff)
// page 2 : show chosen civilian
// page 3 : playing
// page 4 : lose
// page 5 : options
function draw() {
background(220, 80, 80);
for (var i = civilians.length - 1; i >= 0; i--) {
civilians[i].update();
civilians[i].show(mouseX, mouseY);
if (civilians[i].clickedOn(mouseX, mouseY)) {
// detect if is right person
console.log("clicked on boi");
if (civilians[i].isWantedMan()) {
console.log("HES WANTED");
} else {
console.log("HES NOT WANTED");
}
}
}
text(round(frameRate()), 20, 20);
//show wanted man
var tempImg = man1img;
if (wantedMan == 1) {
tempImg = man2img;
} else if (wantedMan == 2) {
tempImg = man3img;
} else if (wantedMan == 3) {
tempImg = man4img;
}
if (wantedMan == 4) {
tempImg = man5img;
} else if (wantedMan == 5) {
tempImg = man6img;
} else if (wantedMan == 6) {
tempImg = aliemanimg;
}
image(tempImg, 50, 70, 70, 90);
}
This is the code for the class:
class Civilian {
constructor(x, y, wantedMan, type) {
this.x = x;
this.y = y;
this.w = 47;
this.h = 60;
this.t = {
x: x,
y: y,
};
this.size = 47;
this.moveSpeed = 0.01;
this.moveDist = 20;
this.wantedMan = wantedMan;
this.civilian = type
this.civilianImg = man1img
this.wantedMan = wantedMan
}
update() {
//move target to random position
this.t.x = random(this.t.x - this.moveDist, this.t.x + this.moveDist);
this.t.y = random(this.t.y - this.moveDist, this.t.y + this.moveDist);
//edge detect
if (this.t.x < 0) {
this.t.x += 5;
}
if (this.t.x > width) {
this.t.x -= 5;
}
if (this.t.y < 0) {
this.t.y += 5;
}
if (this.t.y > height) {
this.t.y -= 5;
}
//images position follows target but with easing
this.x += (this.t.x - this.x) * this.moveSpeed;
this.y += (this.t.y - this.y) * this.moveSpeed;
}
show(ex, ey) {
var d = dist(ex, ey, this.x, this.y);
if (d > this.size / 2) {
tint(255, 255, 255);
} else {
tint(0, 255, 0);
}
if(this.civilian == 1) {
this.civilianImg = man2img
} else if(this.civilian == 2) {
this.civilianImg = man3img
} else if(this.civilian ==3) {
this.civilianImg = man4img
} if(this.civilian == 4) {
this.civilianImg = man5img
} else if(this.civilian == 5) {
this.civilianImg = man6img
} else if(this.civilian == 6) {
this.civilianImg = aliemanimg
}
image(this.civilianImg, this.x, this.y, 47, 60);
}
clickedOn(ex, ey) {
var d = dist(ex, ey, this.x, this.y);
return d < this.size / 2 && mouseIsPressed;
}
isWantedMan() {
return this.civilian == this.wantedMan;
}
}
However, whenever I add a .splice(i,1) under the 'for' loop in setup function - to remove the 'wanted man', it shows this error:
"TypeError: Cannot read properties of undefined (reading
'isWantedMan') at /sketch.js:41:22".
isWantedMan() is a function in the Civilian Class, that returns true if the current 'civilian' is wanted. The .splice is supposed to remove a object from the array, when it is a 'wanted man'.
I don't know why this happens. When I replace the .splice code with a console.log() code, then there is no error.
Also there were probably a lot of things that I could have done better in the code.

Javascript Inheritance, overwriting behavior?

This may be a lengthy post. I'm having to learn JS on the fly and could be making simple mistakes.
I'm writing a game in JS/html, and am using EasyStar to put in some pathfinding behavior for some entities. I'm using inheritance to give the behavior outlined in Walker4 below to multiple entities, example of one at the bottom:
function Walker4(game, img, Ai, lX, lY) {
this.easyStar = Ai;
this.dX = 0;
this.dY = -1;
this.animation = [];
this.animation["NE"] = null;
this.animation["NW"] = null;
this.animation["SE"] = null;
this.animation["SW"] = null;
this.currAnimation = null;
this.facing = "";
this.img = img;
this.isWalking = false;
this.isFindingPath = false;
this.destX = null;
this.destY = null;
this.path = [];
this.next = null;
this.loadCount = 0;
Entity.call(this, game, lX, lY);
}
Walker4.prototype = new Entity();
Walker4.prototype.constructor = Walker4;
Walker4.prototype.update = function () {
if (this.isFindingPath) return;
if (this.isWalking) this.walkPath();
if (this.destX != null && this.destY != null) {
this.isFindingPath = true;
that = this;
easyStar.findPath(this.x, this.y, this.destX, this.destY, function (path) {
if (path === null) {
console.log("No path :(");
} else {
console.log("Path! The first Point is " + path[0].x + " " + path[0].y);
that.path = path;
that.next = that.path.shift();
that.isWalking = true;
}
});
this.destX = null;
this.destY = null;
this.isFindingPath = false;
easyStar.calculate();
}
Entity.prototype.update.call(this);
}
Walker4.prototype.walkPath = function () {
if (this.path.length == 0) {
if (Math.floor(this.x) == this.next.x && Math.floor(this.y) == this.next.y) {
this.dX = 0;
this.dY = 0;
}
isWalking = false;
return;
}
if (Math.floor(this.x) == this.next.x && Math.floor(this.y) == this.next.y) {
this.next = this.path.shift();
this.dX = setDirection(Math.floor(this.x), this.next.x);
this.dY = setDirection(Math.floor(this.y), this.next.y);
this.currAnimation = this.animation[setFace(this.dX, this.dY)];
}
this.x += this.dX * this.game.clockTick * speed;
this.y += this.dY * this.game.clockTick * speed;
}
Walker4.prototype.draw = function (ctx) {
pt1 = twodtoisoX(this.x, this.y) + 27 - this.currAnimation.frameWidth / 2;
pt2 = twodtoisoY(this.x, this.y) + 10 - this.currAnimation.frameHeight / 2;
ctx.fillRect(pt1, pt2, 5, 5);
//console.log(pt1, pt2);
this.currAnimation.drawFrame(this.game.clockTick, ctx, pt1, pt2);
Entity.prototype.draw.call(this);
}
//Cart Walkers
function eCartMan(game, img, Ai, lX, lY) {
Walker4.call(this, game, img, Ai, lX, lY);
this.animation["NE"] = new Animation(img, 0, 0, 60, 48, 12, aSpeed, 12, true);
this.animation["NW"] = new Animation(img, 0, 1, 60, 48, 12, aSpeed, 12, true);
this.animation["SE"] = new Animation(img, 0, 2, 60, 48, 12, aSpeed, 12, true);
this.animation["SW"] = new Animation(img, 0, 3, 60, 48, 12, aSpeed, 12, true);
this.currAnimation = this.animation["NE"];
}
eCartMan.prototype = new Walker4();
eCartMan.prototype.constructor = eCartMan;
All these entities are added to a list of entities that is updated in a loop each game tick, each having update and draw called respectively. Easy Star seems to give each entity a path, but then only the most recently added entity will actually follow their given path. What am I missing?
Any help appreciated.
Alright, scoped down the problem. Thanks to #HMR for helping.
Ended up a scope issue on that = this;, and that each walker needed its own version of the EasyStar pathfinder. that became global, and opened up all instances of the walkers to modifying each other.

HTML Canvas & Javascript - Triggering Audio by Selection (From Multiple Places)

I have a selection menu in my HTML canvas that I would like to trigger corresponding audio files. I have tried implementing this by declaring the images inside the if (this.hovered) & (this.clicked) part of the makeSelection function within the selectionForMenu prototype, such that on each new selection the selected audio file is redefined, but this causes problems like slow loading and overlapping audio. It is also problematic as I am trying to get the speaker button at the bottom of the screen to play the audio corresponding to the current selection too, so if it is only defined within that function it is not accessible to the makeButton function.
You can see the selection menu and speaker button in the snippet below. Each new selection in the menu should play once an audio file that corresponds to it (which I have not been able to add to this demonstration). It can be replayed by re-clicking the selection or clicking the speaker button, but each click should only provoke one play of the audio and of course overlapping is undesired. Any help will be appreciated.
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d');
var button = function(id, x, strokeColor) {
this.id = id;
this.x = x;
this.strokeColor = strokeColor;
this.hovered = false;
this.clicked = false;
}
button.prototype.makeInteractiveButton = function() {
if (this.hovered) {
if (this.clicked) {
this.fillColor = '#DFBCDE';
} else {
this.fillColor = '#CA92C8'
}
} else {
this.fillColor = '#BC77BA'
}
ctx.strokeStyle=this.strokeColor;
ctx.fillStyle=this.fillColor;
ctx.beginPath();
ctx.lineWidth='5';
ctx.arc(this.x, 475, 20, 0, 2*Math.PI);
ctx.closePath();
ctx.stroke();
ctx.fill();
}
button.prototype.hitTest = function(x, y) {
return (Math.pow(x-this.x, 2) + Math.pow(y-475, 2) < Math.pow(20, 2));
}
var selectionForMenu = function(id, text, y) {
this.id = id;
this.text = text;
this.y = y;
this.hovered = false;
this.clicked = false;
this.lastClicked = false;
}
selectionForMenu.prototype.makeSelection = function() {
var fillColor='#A84FA5';
if (this.hovered) {
if (this.clicked) {
if (this.lastClicked) {
fillColor='#E4C7E2';
} else {
fillColor='#D5A9D3';
}
} else if (this.lastClicked) {
fillColor='#D3A4D0';
} else {
fillColor='#BA74B7';
}
} else if (this.lastClicked) {
fillColor='#C78DC5';
} else {
fillColor='#A84FA5';
}
ctx.beginPath();
ctx.fillStyle=fillColor;
ctx.fillRect(0, this.y, 350, 30)
ctx.stroke();
ctx.font='10px Noto Sans';
ctx.fillStyle='white';
ctx.textAlign='left';
ctx.fillText(this.text, 10, this.y+19);
}
selectionForMenu.prototype.hitTest = function(x, y) {
return (x >= 0) && (x <= (350)) && (y >= this.y) && (y <= (this.y+30)) && !((x >= 0) && (y > 450));
}
var Paint = function(element) {
this.element = element;
this.shapes = [];
}
Paint.prototype.addShape = function(shape) {
this.shapes.push(shape);
}
Paint.prototype.render = function() {
ctx.clearRect(0, 0, this.element.width, this.element.height);
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeSelection();
}
catch(err) {}
}
ctx.beginPath();
ctx.fillStyle='#BC77BA';
ctx.fillRect(0, 450, 750, 50);
ctx.stroke();
for (var i=0; i<this.shapes.length; i++) {
try {
this.shapes[i].makeInteractiveButton();
}
catch(err) {}
}
var speaker = new Image(25, 25);
speaker.src='https://i.stack.imgur.com/lXg2I.png';
ctx.drawImage(speaker, 162.5, 462.5);
}
Paint.prototype.setHovered = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].hovered = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setClicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].clicked = this.shapes[i] == shape;
}
this.render();
}
Paint.prototype.setUnclicked = function(shape) {
for (var i=0; i<this.shapes.length; i++) {
this.shapes[i].clicked = false;
if (Number.isInteger(this.shapes[i].id)) {
this.shapes[i].lastClicked = this.shapes[i] == shape;
}
}
this.render();
}
Paint.prototype.select = function(x, y) {
for (var i=this.shapes.length-1; i >= 0; i--) {
if (this.shapes[i].hitTest(x, y)) {
return this.shapes[i];
}
}
return null
}
var paint = new Paint(c);
var btn = new button('speaker', 175, '#FFFCF8');
var selection = [];
for (i=0; i<15; i++) {
selection.push(new selectionForMenu(i+1, i, i*30));
}
paint.addShape(btn);
for (i=0; i<15; i++) {
paint.addShape(selection[i])
}
paint.render();
function mouseDown(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setClicked(shape);
}
function mouseUp(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setUnclicked(shape);
}
function mouseMove(event) {
var x = event.x - canvasX;
var y = event.y - canvasY;
var shape = paint.select(x, y);
paint.setHovered(shape);
}
c.addEventListener('mousedown', mouseDown);
c.addEventListener('mouseup', mouseUp);
c.addEventListener('mousemove', mouseMove);
canvas {
z-index: -1;
margin: 1em auto;
border: 1px solid black;
display: block;
background: #9F3A9B;
}
img {
z-index: 0;
position: absolute;
pointer-events: none;
}
#speaker {
top: 480px;
left: 592px;
}
#snail {
top: 475px;
left: 637.5px;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>uTalk Demo</title>
<link rel='stylesheet' type='text/css' href='wordpractice.css' media='screen'></style>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css">
</head>
<body>
<canvas id="game" width = "350" height = "500"></canvas>
<script type='text/javascript' src='wordpractice copy.js'></script>
</body>
</html>
When you want responsiveness with audio, forget about MediaElements, and go with the Web Audio API.
MediaElements (<audio> and <video>) are slow, and http caching is an nightmare.
With the Web Audio API, you can first download all you media as arrayBuffers, decode their audio data to AudioBuffers, that you'll attach to your js objects.
From there, you'll be able to play new instances of these media in µs.
Beware, ES6 syntax below, for older browsers, here is an ES5 rewrite, also note that Internet Explorer < Edge does not support the Web Audio API, if you need to support these browsers, you'll have to make an fallback with audio elements.
(function myFirstDrumKit() {
const db_url = 'https://dl.dropboxusercontent.com/s/'; // all our medias are stored on dropbox
// we'll need to first load all the audios
function initAudios() {
const promises = drum.parts.map(part => {
return fetch(db_url + part.audio_src) // fetch the file
.then(resp => resp.arrayBuffer()) // as an arrayBuffer
.then(buf => drum.a_ctx.decodeAudioData(buf)) // then decode its audio data
.then(AudioBuf => {
part.buf = AudioBuf; // store the audioBuffer (won't change)
return Promise.resolve(part); // done
});
});
return Promise.all(promises); // when all are loaded
}
function initImages() {
// in this version we have only an static image,
// but we could have multiple per parts, with the same logic as for audios
var img = new Image();
img.src = db_url + drum.bg_src;
drum.bg = img;
return new Promise((res, rej) => {
img.onload = res;
img.onerror = rej;
});
}
let general_solo = false;
let part_solo = false;
const drum = {
a_ctx: new AudioContext(),
generate_sound: (part) => {
// called each time we need to play a source
const source = drum.a_ctx.createBufferSource();
source.buffer = part.buf;
source.connect(drum.gain);
// to keep only one playing at a time
// simply store this sourceNode, and stop the previous one
if(general_solo){
// stop all playing sources
drum.parts.forEach(p => (p.source && p.source.stop(0)));
}
else if (part_solo && part.source) {
// stop only the one of this part
part.source.stop(0);
}
// store the source
part.source = source;
source.start(0);
},
parts: [{
name: 'hihat',
x: 90,
y: 116,
w: 160,
h: 70,
audio_src: 'kbgd2jm7ezk3u3x/hihat.mp3'
},
{
name: 'snare',
x: 79,
y: 192,
w: 113,
h: 58,
audio_src: 'h2j6vm17r07jf03/snare.mp3'
},
{
name: 'kick',
x: 80,
y: 250,
w: 200,
h: 230,
audio_src: '1cdwpm3gca9mlo0/kick.mp3'
},
{
name: 'tom',
x: 290,
y: 210,
w: 110,
h: 80,
audio_src: 'h8pvqqol3ovyle8/tom.mp3'
}
],
bg_src: '0jkaeoxls18n3y5/_drumkit.jpg?dl=0',
};
drum.gain = drum.a_ctx.createGain();
drum.gain.gain.value = .5;
drum.gain.connect(drum.a_ctx.destination);
function initCanvas() {
const c = drum.canvas = document.createElement('canvas');
const ctx = drum.ctx = c.getContext('2d');
c.width = drum.bg.width;
c.height = drum.bg.height;
ctx.drawImage(drum.bg, 0, 0);
document.body.appendChild(c);
addEvents(c);
}
const isHover = (x, y) =>
(drum.parts.filter(p => (p.x < x && p.x + p.w > x && p.y < y && p.y + p.h > y))[0] || false);
function addEvents(canvas) {
let mouse_hovered = false;
canvas.addEventListener('mousemove', e => {
mouse_hovered = isHover(e.pageX - canvas.offsetLeft, e.pageY - canvas.offsetTop)
if (mouse_hovered) {
canvas.style.cursor = 'pointer';
} else {
canvas.style.cursor = 'default';
}
})
canvas.addEventListener('mousedown', e => {
e.preventDefault();
if (mouse_hovered) {
drum.generate_sound(mouse_hovered);
}
});
const checkboxes = document.querySelectorAll('input');
checkboxes[0].onchange = function() {
general_solo = this.checked;
general_solo && (checkboxes[1].checked = part_solo = true);
};
checkboxes[1].onchange = function() {
part_solo = this.checked;
!part_solo && (checkboxes[0].checked = general_solo = false);
};
}
Promise.all([initAudios(), initImages()])
.then(initCanvas);
})()
/*
Audio Samples are from https://sampleswap.org/filebrowser-new.php?d=DRUMS+%28FULL+KITS%29%2FSpasm+Kit%2F
Original image is from http://truimg.toysrus.co.uk/product/images/UK/0023095_CF0001.jpg?resize=500:500
*/
<label>general solo<input type="checkbox"></label><br>
<label>part solo<input type="checkbox"></label><br>
You could create an Audio Loader, that loads all the audios and keeps track of them:
function load(srcs){
var obj={};
srcs.forEach(src=>obj[src]=new Audio(src));
return obj;
}
Then you could do sth like this onload:
var audios=load(["audio1.mp3", "audio2.mp3"]);
And later:
(audios[src] || new Audio(src)).play();
This will just load the audio if it isnt already in the audios object.

How can I get this top down shooter I made running?

Spent 6 hours today building this from a tutorial modifying it for my elements, I've been trying for hours to get it to work and i Just can't :/
Here is a link to the tutorial i was following :
http://gamedevelopment.tutsplus.com/tutorials/create-a-simple-space-shooter-game-in-html5-with-easeljs--active-10944
Zip of all the files including the gifs etc. :
http://www.filedropper.com/shooter_1
Raw Javascript Code:
/* Variable Declarations */
var Lives = new Container(); //stores the lives gfx
var Bullets = new Container(); //stores the bullets gfx
var Enemies = new Container(); //stores the enemies gfx
var Boss_Health = 20;
var Score;
var Gfx_Loaded = 0; //used as a preloader, counts the already loaded items
var Center_X = 960;
var Center_Y = 540;
var Ticker_Listener = new Object(); //used as a Ticker listener
var Timer_Source; //references a setInterval method
/* Define Canvas*/
var canvas;
var stage;
/* Background */
var BG_Img = new Image();
var BG;
var BG2_Img = new Image();
var BG2;
/* Characters */
var Player1_Img = new Image();
var Player1;
/* Enemies */
var Enemy1_Img = new Image();
/* Boss */
var Boss1_Img = new Image();
var Boss1;
/* Lives */
var Life_Img = new Image();
/* Bullets */
var Bullet_Img1 = new Image();
/* Alerts */
var Win_Img = new Image ();
var Lose_Img = new Image ();
var Win;
var Lose;
/*Initiation Function*/
function Main () {
canvas = document.getElementbyId('Shooter');
stage = new stage(canvas);
stage.mouseEventsEnabled = true;
/*Sounds*/
SoundJS.addBatch([
{name:'Boss', src:'Boss.mp3', instances:1},
{name:'Shuriken_Hit', src:'Shuriken_Hit.mp3', instances:10},
{name:'Kunai_Throw', src:'Kunai_Throw.mp3', instances:10}]);
/* Load GFX */
BG_Img.src = 'BG_Img.jpg';
BG_Img.name = 'BG';
BG_Img.onload = loadGfx;
BG2_Img.src = 'BG2_Img.jpg';
BG2_Img.name = 'BG2';
BG2_Img.onload = loadGfx;
Player1_Img.src = 'Naruto_Idle.gif';
Player1_Img.name = 'Player1';
Player1_Img.onload = loadGfx;
Enemy1_Img.src = 'Basic_Enemy.gif';
Enemy1_Img.name = 'Enemy1';
Enemy1_Img.onload = loadGfx;
Boss1_Img.src = 'Akatsuki_Boss.gif';
Boss1_Img.name = 'Boss1';
Boss1_Img.onload = loadGfx;
Life_Img.src = 'Life.gif';
Life_Img.name = 'Life';
Life_Img.onload = loadGfx;
Bullet_Img1.src = 'Rasengan_Mid_Air.gif';
Bullet_Img1.name = 'Bullet';
Bullet_Img1.onload = loadGfx;
Win_Img.src = 'Game_Win.gif';
Win_Img.name = 'Game_Win';
Win_Img.onload = loadGfx;
Lose_Img.src = 'Game_Over.gif';
Lose_Img.name = 'Game_Over';
Lose_Img.onload = loadGfx;
/* Ticker */
Ticker.setFPS(30);
Ticker.addListener(stage);
}
/*Preload Function */
function loadGfx(e)
{
if(e.target.name = 'BG'){BG = new Bitmap(BG_Img);}
if(e.target.name = 'BG2'){BG2 = new Bitmap(BG2_Img);}
if(e.target.name = 'Player1'){Player1 = new Bitmap(Player1_Img);}
gfxLoaded++;
if(gfxLoaded == 9)
{
addGameView();
}
}
/* Game View Function */
function addGameView()
{
Player1.x = Center_X - 18.5;
Player1.y = 480 + 34;
/* Add Lives */
for(var i = 0; i < 3; i++)
{
var l = new Bitmap(Life_Img);
l.x = 248 + (25 * i);
l.y = 463;
Lives.addChild(l);
stage.update();
}
/* Score Text */
Score = new Text('0', 'bold 14px Courier New', '#FFFFFF');
Score.maxWidth = 1000; //fix for Chrome 17
Score.x = 2;
Score.y = 476;
/* Second Background */
BG2.y = -480;
/* Add gfx to stage and Tween Ship */
stage.addChild(BG, BG2, Player1, Enemies, Bullets, Lives, Score);
Tween.get(Player1).to({y:425}, 1000).call(Start_Game);
}
/*Move the Player*/
function Move_Player1(e)
{
Player1.x = e.stageX - 18.5;
}
/* Shooting */
function Shoot()
{
var b = new Bitmap(Bullet_Img1);
b.x = Player1.x + 13;
b.y = Player1.y - 20;
Bullets.addChild(b);
stage.update();
SoundJS.play('Kunai_Throw');
}
/* Adding Enemies */
function Add_Enemy ()
{
var e = new Bitmap(Enemy1_Img);
e.x = Math.floor(Math.random() * (320-50))
e.y = -50
Enemies.addChild(e);
stage.update();
}
function Start_Game()
{
stage.onMouseMove = Move_Player1;
BG.onPress = Shoot;
BG2.onPress = Shoot;
Ticker.addListener(Ticker_Listener, false);
Ticker_Listener.tick = update;
Timer_Source = setInterval('addEnemy()', 1000);
}
function update()
{
/* Move Background */
BG.y += 5;
BG2.y += 5;
if(BG.y >= 480)
{
BG.y = -480;
}
else if(BG2.y >= 480)
{
BG2.y = -480;
}
/* Move Bullets */
for(var i = 0; i < Bullets.children.length; i++)
{
Bullets.children[i].y -= 10;
}
/* Clear offstage Bullets */
if(Bullets.children[i].y < - 20)
{
Bullets.removeChildAt(i);
}
}
/* Show Boss */
if(parseInt(Score.text) >= 1000 && Boss1 == null)
{
Boss1 = new Bitmap(Boss1_Img);
SoundJS.play('Boss');
Boss1.x = Center_X - 90;
Boss1.y = -183;
stage.addChild(boss1);
Tween.get(boss1).to({y:40}, 2000) //tween the boss onto the play area
}
/* Move Enemies */
for(var j = 0; j < Enemies.children.length; j++)
{
eEemies.children[j].y += 5;
/* Remove Offstage Enemies */
if(Enemies.children[j].y > 480 + 50)
{
Enemies.removeChildAt(j);
}
}
/* Bullet - Enemy Collision */
for(var k = 0; k < Bullets.children.length; k++)
{
if(Bullets.children[k].x >= Enemies.children[j].x && Bullets.children[k].x + 11 < Enemies.children[j].x + 49 && Bullets.children[k].y < Enemies.children[j].y + 40)
{
Bullets.removeChildAt(k);
Enemies.removeChildAt(j);
stage.update();
SoundJS.play('Shuriken_Hit');
Score.text = parseFloat(Score.text + 50);
}
/* Bullet - Boss Collision */
if(Boss1 != null && bullets.children[k].x >= Boss1.x && Bullets.children[k].x + 11 < Boss1.x + 183 && Bullets.children[k].y < Boss1.y + 162)
{
Bullets.removeChildAt(k);
Boss_Health--;
stage.update();
SoundJS.play('Shuriken_Hit');
Score.text = parseInt(Score.text + 50);
}
}
/* Player1 - Enemy Collision */
if(Enemies.hitTest(Player1.x, Player1.y) || Enemies.hitTest(Player1.x + 37, Player1.y))
{
Enemies.removeChildAt(j);
Lives.removeChildAt(Lives.length);
Player1.y = 480 + 34;
Tween.get(Player1).to({y:425}, 500)
SoundJS.play('Shuriken_Hit');
}
/* Check for win */
if(Boss1 != null && Boss_Health <= 0)
{
alert('Win');
}
/* Check for lose */
if(Lives.children.length <= 0)
{
alert('Lose');
}
function alert(e)
{
/* Remove Listeners */
stage.onMouseMove = null;
BG.onPress = null;
BG2.onPress = null;
Ticker.removeListener(Ticker_Listener);
Ticker_Listener = null;
Timer_Source = null;
/* Display Correct Message */
if(e == 'Win')
{
Win = new Bitmap(Win_Img);
Win.x = centerX - 64;
Win.y = centerY - 23;
stage.addChild(Win);
stage.removeChild(Enemies, Boss1);
}
else
{
Lose = new Bitmap(Lose_Img);
Lose.x = centerX - 64;
Lose.y = centerY - 23;
stage.addChild(Lose);
stage.removeChild(Enemies, Player1);
}
BG.onPress = function(){window.location.reload();};
BG2.onPress = function(){window.location.reload();};
stage.update();
}
Raw HTML Code:
<!doctype html>
<html>
<head>
<title>Scrolling Shooter</title>
<meta charset="utf-8">
<style>*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}</style>
<link rel="stylesheet" href="Week11.css">
<script src="https://code.createjs.com/easeljs-0.8.1.min.js"></script>
<script src="https://code.createjs.com/tweenjs-0.6.1.min.js"></script>
<script src="https://code.createjs.com/soundjs-0.6.1.min.js"></script>
</head>
<body onload="Main();">
<div id="stage">
<canvas id= "Shooter"> width="1920" height="1080" </canvas>
</div>
<script src="Main.js"> </script>
</body>
</html>
The tutorial you are using is sorely out-of-date (it uses a version of CreateJS that is 3 or 4 years old). The version of EaselJS you are using in your sample was published in May 2015.
To start with, all CreateJS classes (such as Container) need to use the createjs namespace:
Player1 = new createjs.Bitmap(Player1_Img);
Score = new createjs.Text('0', 'bold 14px Courier New', '#FFFFFF');
The Ticker API has also changed:
createjs.Ticker.addEventListener("tick", stage);
// OR
createjs.Ticker.on("tick", stage);
There are also other APIs that have changed (BitmapAnimation became Sprite for example).
I would start there, and then follow up here once you have made some progress. Use your console to determine what errors are happening, and solve them one at a time.
Cheers,

box2dweb raycast

I am trying to get the distance between my character and the ground, I have found something that looks like it should do what I want but it has been written for another version of box2d.
Original:
float targetHeight = 3;
float springConstant = 100;
//make the ray at least as long as the target distance
b2Vec2 startOfRay = m_hovercarBody->GetPosition();
b2Vec2 endOfRay = m_hovercarBody->GetWorldPoint( b2Vec2(0,-5) );
overcarRayCastClosestCallback callback;
m_world->RayCast(&callback, startOfRay, endOfRay);
if ( callback.m_hit ) {
float distanceAboveGround = (startOfRay - callback.m_point).Length();
//dont do anything if too far above ground
if ( distanceAboveGround < targetHeight ) {
float distanceAwayFromTargetHeight = targetHeight - distanceAboveGround;
m_hovercarBody->ApplyForce( b2Vec2(0,springConstant*distanceAwayFromTargetHeight),
m_hovercarBody->GetWorldCenter() );
}
}
I have tried to change it to what I think it should be but it doesn't even call the callback.
var targetHeight = 3;
var springConstant = 100;
//make the ray at least as long as the target distance
startOfRay = new b2Vec2(m_hovercarBody.GetPosition());
endOfRay = new b2Vec(m_hovercarBody.GetWorldPoint( b2Vec2(0,-5)));
function callback(raycast){
if ( raycast.m_hit ) {
var distanceAboveGround = (startOfRay - raycast.m_point).Length();
//dont do anything if too far above ground
if ( distanceAboveGround < targetHeight ) {
var distanceAwayFromTargetHeight = targetHeight - distanceAboveGround;
m_hovercarBody.ApplyForce( b2Vec2(0,springConstant*distanceAwayFromTargetHeight),
m_hovercarBody.GetWorldCenter() );
}
}
}
m_world.RayCast(callback, startOfRay, endOfRay);
Any idea how to convert it to work with box2dweb?
Thanks
It might be that the original bit of code was written for a platform where the coordinate system works differently.
In a Canvas element, the coordinate system starts from the top left corner, meaning that m_hovercarBody.GetWorldPoint( b2Vec2(0,-5)) is checking for a point above the character, rather than below.
I'm not sure about the rest of the code but try changing that to m_hovercarBody.GetWorldPoint( b2Vec2(0,5)) and see what happens.
EDIT:
I think actually the problem is with the way you've structured your callback. Looking up the reference for the Raycast function would reveal more.
(The Javascript version of Box2D you're using is an automatic port of the Actionscript one. Given the two have fairly similar syntax, you can use the reference for Flash.)
The original code you posted seems to be C++, but I don't know much about its syntax. It seems there's some sort of class that does the raycasting (overcarRayCastClosestCallback). You can either look for that, or try and build your own callback function according to the first link I posted. It would be something along the lines of:
function customRaycastCallback(fixture, normal, fraction) {
// you can, for instance, check if fixture belongs to the ground
// or something else, then handle things accordingly
if( /* fixture belongs to ground */ ) {
// you've got the fraction of the original length of the raycast!
// you can use this to determine the distance
// between the character and the ground
return fraction;
}
else {
// continue looking
return 1;
}
}
Try running this on your browser. I coded this when I was learning sensor and RAYCAST in Box2Dweb. Hope it helps.
<html>
<head>
<title>Box2dWeb Demo</title>
</head>
<body>
<canvas id="canvas" width="600" height="420" style="background-color:#333333;" ></canvas>
<div id="cc" style="position:absolute; right:0; top:100px; width:500px; height:50px; margin:0;"></div>
</body>
<script type="text/javascript" src="Box2dWeb-2.1.a.3.js"></script>
<script type="text/javascript" src="jquery-1.7.2.js"></script>
<script type="text/javascript">
var b2Vec2 = Box2D.Common.Math.b2Vec2
, b2BodyDef = Box2D.Dynamics.b2BodyDef
, b2Body = Box2D.Dynamics.b2Body
, b2FixtureDef = Box2D.Dynamics.b2FixtureDef
, b2World = Box2D.Dynamics.b2World
, b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
, b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
, b2ContactFilter = Box2D.Dynamics.b2ContactFilter
, b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef
, b2DebugDraw = Box2D.Dynamics.b2DebugDraw
, b2Fixture = Box2D.Dynamics.b2Fixture
, b2AABB = Box2D.Collision.b2AABB
, b2WorldManifold = Box2D.Collision.b2WorldManifold
, b2ManifoldPoint = Box2D.Collision.b2ManifoldPoint
, b2RayCastInput = Box2D.Collision.b2RayCastInput
, b2RayCastOutput = Box2D.Collision.b2RayCastOutput
, b2Color = Box2D.Common.b2Color;
var world = new b2World(new b2Vec2(0,10), true);
var canvas = $('#canvas');
var context = canvas.get(0).getContext('2d');
//box
var bodyDef = new b2BodyDef;
bodyDef.type = b2Body.b2_dynamicBody;
bodyDef.position.Set(9,7);
bodyDef.userData = 'box';
var fixDef = new b2FixtureDef;
fixDef.filter.categoryBits = 1;
fixDef.density = 10.0;
fixDef.friction = 0.5;
fixDef.restitution = .5;
fixDef.shape = new b2PolygonShape;
fixDef.shape.SetAsBox(1,5);
var box1 = world.CreateBody(bodyDef);
box1.CreateFixture(fixDef);
//circle
var bodyDef2 = new b2BodyDef;
bodyDef2.type = b2Body.b2_dynamicBody;
bodyDef2.position.Set(4,8);
bodyDef2.userData = 'obj';
var fixDef2 = new b2FixtureDef;
fixDef2.filter.categoryBits = 2;
fixDef2.filter.maskBits = 13;
fixDef2.density = 10.0;
fixDef2.friction = 0.5;
fixDef2.restitution = .2;
fixDef2.shape = new b2CircleShape(1);
//circlesensor
var cc = new b2FixtureDef;
cc.shape = new b2CircleShape(2);
cc.shape.SetLocalPosition(new b2Vec2(0 ,0));
cc.density = 0;
cc.isSensor = true;
cc.filter.categoryBits = 8;
var wheel = world.CreateBody(bodyDef2);
wheel.CreateFixture(fixDef2);
wheel.CreateFixture(cc);
//create a ground
var holderDef = new b2BodyDef;
holderDef.type = b2Body.b2_staticBody;
holderDef.userData = "ground";
holderDef.position.Set(10, 14);
var fd = new b2FixtureDef;
fd.filter.categoryBits = 4;
fd.shape = new b2PolygonShape;
fd.shape.SetAsBox(10,1);
var ground = world.CreateBody(holderDef);
ground.CreateFixture(fd);
//create another static body
var holderDef = new b2BodyDef;
holderDef.type = b2Body.b2_staticBody;
holderDef.position.Set(10, 20);
var temp = world.CreateBody(holderDef);
temp.CreateFixture(fd);
var c=0;
$(window).keydown(function(e) {
$('#aa').html(++c);
code = e.keyCode;
if(c==1) {
if(code == 38 && onground)
wheel.SetLinearVelocity(new b2Vec2(0,-10));
if(code == 39)
wheel.ApplyForce(new b2Vec2(1000,0), box1.GetWorldPoint(new b2Vec2(0,0)));
if(code == 37)
wheel.ApplyForce(new b2Vec2(-1000,0), box1.GetWorldPoint(new b2Vec2(0,0)));
}
});
$(window).keyup(function(e) {
c=0;
});
var listener = new Box2D.Dynamics.b2ContactListener;
listener.BeginContact = function(contact) {
if(contact.GetFixtureA().GetBody().GetUserData()== 'obj' || contact.GetFixtureB().GetBody().GetUserData()== 'obj' ) // think about why we don't use fixture's userData directly.
onground = true;// don't put 'var' here!
fxA=contact.GetFixtureA();
fxB=contact.GetFixtureB();
sA=fxA.IsSensor();
sB=fxB.IsSensor();
if((sA && !sB) || (sB && !sA)) {
if(sA) {
$('#cc').prepend(contact.GetFixtureB().GetBody().GetUserData() + ' is in the viscinity of body '+contact.GetFixtureA().GetBody().GetUserData()+'<br>');
}
else {
$('#cc').prepend(contact.GetFixtureA().GetBody().GetUserData() + ' is in the viscinity of body '+contact.GetFixtureB().GetBody().GetUserData()+'<br>');
}
}
}
listener.EndContact = function(contact) {
if (contact.GetFixtureA().GetBody().GetUserData()== 'obj' || contact.GetFixtureB().GetBody().GetUserData()== 'obj' )
onground = false;
}
var debugDraw = new b2DebugDraw();
debugDraw.SetSprite ( document.getElementById ("canvas").getContext ("2d"));
debugDraw.SetDrawScale(30); //define scale
debugDraw.SetAlpha(1);
debugDraw.SetFillAlpha(.3); //define transparency
debugDraw.SetLineThickness(1.0);
debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
world.SetDebugDraw(debugDraw);
window.setInterval(update,1000/60);
//mouse
var mouseX, mouseY, mousePVec, isMouseDown, selectedBody, mouseJoint;
var canvasPosition = getElementPosition(document.getElementById("canvas"));
document.addEventListener("mousedown", function(e) {
isMouseDown = true;
handleMouseMove(e);
document.addEventListener("mousemove", handleMouseMove, true);
}, true);
document.addEventListener("mouseup", function() {
document.removeEventListener("mousemove", handleMouseMove, true);
isMouseDown = false;
mouseX = undefined;
mouseY = undefined;
}, true);
function handleMouseMove(e) {
mouseX = (e.clientX - canvasPosition.x) / 30;
mouseY = (e.clientY - canvasPosition.y) / 30;
};
function getBodyAtMouse() {
mousePVec = new b2Vec2(mouseX, mouseY);
var aabb = new b2AABB();
aabb.lowerBound.Set(mouseX - 0.001, mouseY - 0.001);
aabb.upperBound.Set(mouseX + 0.001, mouseY + 0.001);
// Query the world for overlapping shapes.
selectedBody = null;
world.QueryAABB(getBodyCB, aabb);
return selectedBody;
}
function getBodyCB(fixture) {
if(fixture.GetBody().GetType() != b2Body.b2_staticBody) {
if(fixture.GetShape().TestPoint(fixture.GetBody().GetTransform(), mousePVec)) {
selectedBody = fixture.GetBody();
return false;
}
}
return true;
}
//at global scope
var currentRayAngle = 0;
var input = new b2RayCastInput();
var output = new b2RayCastOutput();
var b = new b2BodyDef();
var f = new b2FixtureDef();
var closestFraction = 1;
var intersectionNormal = new b2Vec2(0,0);
var intersectionPoint = new b2Vec2();
rayLength = 25; //long enough to hit the walls
var p1 = new b2Vec2( 11, 7 ); //center of scene
var p2 = new b2Vec2();
var normalEnd = new b2Vec2();
function update() {
if(isMouseDown && (!mouseJoint)) {
var body = getBodyAtMouse();
if(body) {
var md = new b2MouseJointDef();
md.bodyA = world.GetGroundBody();
md.bodyB = body;
md.target.Set(mouseX, mouseY);
md.collideConnected = true;
md.maxForce = 300.0 * body.GetMass();
mouseJoint = world.CreateJoint(md);
body.SetAwake(true);
}
}
if(mouseJoint) {
if(isMouseDown) {
mouseJoint.SetTarget(new b2Vec2(mouseX, mouseY));
} else {
world.DestroyJoint(mouseJoint);
mouseJoint = null;
}
}
world.Step(1 / 60, 10, 10);
world.DrawDebugData();
world.ClearForces();
world.SetContactListener(listener);
ray();
};
function ray() {
//in Step() function
var k = 360/20;
var t = k/60;
var DEGTORAD = Math.PI/180;
currentRayAngle += t * DEGTORAD; //one revolution every 20 seconds
//console.log(currentRayAngle*(180/Math.PI));
//calculate points of ray
p2.x = p1.x + rayLength * Math.sin(currentRayAngle);
p2.y = p1.y + rayLength * Math.cos(currentRayAngle);
input.p1 = p1;
input.p2 = p2;
input.maxFraction = 1;
closestFraction = 1;
var b = new b2BodyDef();
var f = new b2FixtureDef();
for(b = world.GetBodyList(); b; b = b.GetNext()) {
for(f = b.GetFixtureList(); f; f = f.GetNext()) {
if(!f.RayCast(output, input))
continue;
else if(output.fraction < closestFraction) {
closestFraction = output.fraction;
intersectionNormal = output.normal;
}
}
}
intersectionPoint.x = p1.x + closestFraction * (p2.x - p1.x);
intersectionPoint.y = p1.y + closestFraction * (p2.y - p1.y);
normalEnd.x = intersectionPoint.x + intersectionNormal.x;
normalEnd.y = intersectionPoint.y + intersectionNormal.y;
context.strokeStyle = "rgb(255, 255, 255)";
context.beginPath(); // Start the path
context.moveTo(p1.x*30,p1.y*30); // Set the path origin
context.lineTo(intersectionPoint.x*30, intersectionPoint.y*30); // Set the path destination
context.closePath(); // Close the path
context.stroke();
context.beginPath(); // Start the path
context.moveTo(intersectionPoint.x*30, intersectionPoint.y*30); // Set the path origin
context.lineTo(normalEnd.x*30, normalEnd.y*30); // Set the path destination
context.closePath(); // Close the path
context.stroke(); // Outline the path
}
//helpers
//http://js-tut.aardon.de/js-tut/tutorial/position.html
function getElementPosition(element) {
var elem=element, tagname="", x=0, y=0;
while((typeof(elem) == "object") && (typeof(elem.tagName) != "undefined")) {
y += elem.offsetTop;
x += elem.offsetLeft;
tagname = elem.tagName.toUpperCase();
if(tagname == "BODY")
elem=0;
if(typeof(elem) == "object") {
if(typeof(elem.offsetParent) == "object")
elem = elem.offsetParent;
}
}
return {x: x, y: y};
}
</script>
</html>
Here's a Raycast in Box2dWeb that I got working:
var p1 = new b2Vec2(body.GetPosition().x, body.GetPosition().y); //center of scene
var p2 = new b2Vec2(body.GetPosition().x, body.GetPosition().y + 5); //center of scene
world.RayCast(function(x){
console.log("You've got something under you");
}, p1,p2);

Categories