coding 2D game physics in javascript, not-flat ground and rotation - javascript

I have a small project in my webprogramming course where I am supposed to make a small game in javascript. This is the game: two cannons are fixed on
opposing ends of the game space. The cannons take turns shooting at each other, and the player tilts the cannon to some degree and chooses a force to shoot the cannonball with. There's a bunch of other stuff I'm thinking of that will be added if there's time.
I found a nice site, http://www.rodedev.com/tutorials/gamephysics/, to get me going with 2d physics and I've made some tests, see the code below. I do however have two big problems:
Non-flat floor/ground.
Rotation!
Right now the script is only checking for the style.top value of the floorDiv, so the box will always "land" on this row of pixels. If I want to have
variable-height terrain, this won't suffice. Any ideas from you guys on how this could be accomplished? Is there anyway to check the color at a specific pixel? If so I could check for non-sky or ground colored pixels and make this the landing critera.
I need to rotate the cannon so players can adjust their aim! I'm thinking I'll make two different cannon objects, the cannon "base" and the cannon barrel. Then I could just rotate the cannon barrel when the player presses a key. Unfortunately rotation seems to be hard in webprogramming. I found this jquery plugin, at http://code.google.com/p/jquery-rotate/, but I'm not getting it to work quite as I would like. Even though I've made sure the cannonball is centered in the picture there is also some translation, not just rotation.
I suppose this might be because the picture doesn't rotate about it's center? See the rotation test code below (you'll have to download
the rotation plugin if you want to try the code).
I suppose I could just make a picture for each angle but that seems kind of wasteful
since there's no actual visual change to the cannon otherwise.
Also, if I want to animate several objects at the same time, do you think I should just change all the objects in the same gameloop function?
Any ideas on this?
The following is the code for the physics-testing
CSS-file
#moveDiv {
position:absolute;
width:100px;
height:100px;
background-color:#000;
}
#floorDiv {
position:absolute;
width:800px;
height:1px;
background-color:#2E2;
}
table {
text-align:right;
}
table input {
width:35px;
}
HTML-file
<script type="text/javascript">
//global vars
var gravity = 0.5;
var friction = 0.5;
var moving = false;
var moveDiv;
var floorDiv;
var yPos;
var xPos;
var floorPos;
var velocity_y = 0;
var velocity_x = 0;
</script>
</head>
<body onload="initialize();">
<input type="button" value="fall" onclick="fall();"></button>
<input type="button" value="Push" onclick="push();"></button>
<input type="button" value="reset" onclick="reset();"></button>
<table>
<tr>
<td>Angle: <input type="text" value="45" id="angle" /></td>
<td>Speed: <input type="text" value="10" id="speed"/></td>
</tr>
<tr>
<td>Gravity: <input type="text" value="0.5" id="gravity" /></td>
<td>Friction: <input type="text" value="0.5" id="friction" /></td>
</table>
<div id="moveDiv"></div>
<div id="floorDiv"></div>
</body>
</html>
js-file
function initialize() {
moveDiv = document.getElementById('moveDiv');
floorDiv = document.getElementById('floorDiv');
moveDiv.style.left=400;
moveDiv.style.top=50;
floorDiv.style.left=200;
floorDiv.style.top=400;
}//end initialize
function fall()
{
moving=true;
var angle = 275;
var rad = angle / (Math.pi * 2);
var scale_x = Math.cos(rad);
var scale_y = Math.sin(rad);
var moveDivSpeed = 0;
gameLoop();
}//end fall
function push()
{
moving=true;
var angle = document.getElementById('angle').value;
var rad = angle / (Math.PI * 2);
var scale_x = Math.cos(rad);
var scale_y = Math.sin(rad);
var moveDivSpeed = document.getElementById('speed').value;
friction = document.getElementById('friction').value;
gravity = document.getElementById('gravity').value;
velocity_x = moveDivSpeed*scale_x;
console.log("original velocity_x is " + velocity_x);
velocity_y = moveDivSpeed*scale_y;
gameLoop();
}
function gameLoop () {
//console.log("gameLoop start");
var len = moveDiv.style.top.length;
var presentTop = parseInt(moveDiv.style.top.substr(0, len-2));
var lenX = moveDiv.style.left.length;
var presentLeft = parseInt(moveDiv.style.left.substr(0, lenX-2));
var len2 = floorDiv.style.top.length;
var floorTop = parseInt(floorDiv.style.top.substr(0, len2-2));
if (moving == true)
{
velocity_y -= gravity;
if ((presentTop+100) - velocity_y < floorTop)
{ //if moveDiv hasn't hit the floor yet...
moveDiv.style.top = presentTop - velocity_y;
moveDiv.style.left = presentLeft + velocity_x;
}
else if (presentTop + 100 == floorTop) //if moveDiv is ON the floor
{
velocity_x = velocity_x*(1-friction);
moveDiv.style.left = presentLeft + velocity_x;
console.log("on the floor");
console.log("friction is " + friction);
console.log(" and velocity_x is " + velocity_x);
console.log("moveDiv.style.left is " +moveDiv.style.left);
if (velocity_x <= 1)
{
console.log("stopped moving");
moving = false;
}
}
else //if moveDiv will hit the floor/go through the floor this time
{
var diff = floorTop - (presentTop + 100);
moveDiv.style.top = presentTop + diff;
moveDiv.style.left = presentLeft + velocity_x;
}
}
else if (moving == false)
{
clearTimeout(runAgain);
console.log("else if moving == false");
return false;
}
var runAgain = setTimeout("gameLoop()", 5);
//console.log("end of gameLoop");
return false;
}//end gameLoop
function reset () {
moving = false;
moveDiv.style.left=400;
moveDiv.style.top=50;
floorDiv.style.left=200;
floorDiv.style.top=400;
}//end reset
Code for the rotation-test(just the html file and the rotation plugin)
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script type="text/javascript" src="jquery.rotate.1-1.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#hide").click(function() {
$("p").hide(500);
$(".box").animate({height:300, opacity:0}, 1000);
});
$("#show").click(function() {
$("p").show(500);
$(".box").animate({height:100, opacity:100}, 1000);
});
$(".box").mouseenter(function() {
$(".box").animate({height:300, opacity:0}, 500);
});
$(".box").mouseleave(function() {
$(".box").animate({height:100, opacity:10}, 1000);
});
$("#move").click(function() {
$("#grey").animate({left: -300, top: -100}, 1000)
.animate({left: -600, top: 200}, 1000);
});
$("#cannonball").click(function() {
$("#cannonball").animate({"left": "+=300", "top": "-=100"}, 500)
.animate({"left": "+=300", "top": "+=100"}, 500);
});
$("p.fading").css("opacity","0.3");
$("p.fading").hover(function() {
$(this).stop().animate({opacity: 1.0}, "slow");},
function () {
$(this).stop().animate({opacity: 0.3}, "slow");
});
$("#rotateRight").click(function() {
$("#cannonball").rotate(10);
});
}); //end jquery document.ready scripts
function initialize () {
document.getElementById('box').style.top = 250;
}
</script>
<style type="text/css">
body {
background-color: #ccc;
}
.box {
background-color: #000;
width:100px;
height:100px;
margin-left:300px;
}
.divMove {
position:relative;
top:350px;
float:right;
background-color: #ddd;
width:100px;
height:100px;
margin-right:100px;
}
#cannonball {
position: relative;
}
</style>
</head>
<body onload="initialize();">
<p>Let's make this disappear and appear.</p>
<button id="hide">Hide</button>
<button id="show">Show</button>
<button id="move">Move</button>
<button id="rotateRight">Rotate+</button>
<div id="box" class="box">
</div>
<div class="divMove" id="grey">
</div>
<img src="http://dl.dropbox.com/u/18833130/Webprogrammering/test/cannonball.png" id="cannonball" border="1" />
<p class="fading">This is a paragraph that fades. Let's see how it looks.</p>
</body>
</html>
I apologize for the long post and the amount of code, but if you want to try it you can basically just copy-paste, and I thought it would be easier to discuss this way!

I'm thinking I'll make two different cannon objects, the cannon "base" and the cannon barrel. Then I could just rotate the cannon barrel when the player presses a key. Unfortunately rotation seems to be hard in webprogramming.
The easiest (now) would be to use the CSS transform property:
const angleInputElement = document.getElementById('angle');
const degrees = angleInputElement.value;
const barrelElement = document.getElementById('barrel'); // I don't see this in your sample though
barrelElement.style.transform = `rotate(${degrees}deg)`;
Also, if I want to animate several objects at the same time, do you think I should just change all the objects in the same gameloop function? Any ideas on this?
For this simple implementation that should work fine.

Related

Draw rectangle over uploaded image

Once I upload an image I make 2 clicks over it. That's how (x1,y1) and (x2,y2) are obtained. Given these 4 numbers I waanna draw a rectangle over the image by means for example of P5.js. Then I should say rect(x1,y1,x2,y2) but this never happens. How can I deal with this problem (maybe not by P5)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Vue Test</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css">
</head>
<body>
<div id="app">
<button v-on:click="isHidden = false">Load img</button>
<img onclick="showCoords(event)" v-if="!isHidden" v-bind:src="src[z]"></img>
</div>
<script>
var test = new Vue({
el: '#app',
data: {
src: ["cat.jpg", "dog.jpg"],
z: Math.round(Math.random()),
isHidden: true,
}
})
var k=0;
var koors = [];
var flag = false;
function showCoords(event) {
if (k===0){
koors[0] = event.clientX;
koors[1] = event.clientY;
k+=1;
} else if (k===1){
flag = true;
koors[2] = event.clientX;
koors[3] = event.clientY;
}
if ((koors[3] != 0) && (flag)){
console.log(koors)
}
}
//p5
function setup(){
}
function draw(){
console.log(koors[0],koors[1],koors[2],koors[3]);
rect(koors[0],koors[1],koors[2],koors[3])
}
</script>
<script src="p5.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.13/dist/vue.js"></script>
<script src="js.js"></script>
</body>
</html>
P5 needs a canvas for rendering. If you don't initialize one, it creates it himself (and you are in trouble).
Also, P5 is powerful library, it has tools for events, image processing etc. For current example I wouldn't use any other library (vue).
I created canvas on top of the image (css) and rest is playing with P5.
var test = new Vue({
el: '#app',
data: {
src: [ "https://i.stack.imgur.com/XZ4V5.jpg", "https://i.stack.imgur.com/7bI1Y.jpg"],
z: Math.round(Math.random()),
isHidden: false,
}
})
var recStart;
var coords = {};
/*******************************************************************
* P5 setup
* run once, use for initialisation.
*
* Create a canvas, change dimensions to
* something meaningful(like image dim)
********************************************************************/
function setup(){
var canvas = createCanvas(480, 320);
canvas.parent('app');
}
/*******************************************************************
* P5 draw
* endless loop.
*
* Lets redraw rectangle until second click.
********************************************************************/
function draw(){
if(recStart)
drawRect();
}
/*******************************************************************
* drawRect
*
* Draw a rectangle. mouseX and mouseY are P5 variables
* holding mouse current position.
********************************************************************/
function drawRect(){
clear();
noFill();
stroke('red');
rect(coords.x, coords.y, mouseX-coords.x, mouseY-coords.y);
}
/*******************************************************************
* P5 mouseClicked
*
* P5 event. Again, mouseX and mouseY are used.
********************************************************************/
mouseClicked = function() {
if (mouseButton === LEFT) {
if(!recStart){ // start rectangle, give initial coords
coords.x = mouseX;
coords.y = mouseY;
recStart = true; // draw() starts to draw
} else {
recStart = false; // stop draw()
drawRect(); // draw final rectangle
coords = {}; // clear coords
}
}
};
canvas {
width: 500px;
height:500px;
z-index: 2;
border:1px solid;
position:absolute;
left: 0;
top: 0;
}
img {
z-index: 1;
position: absolute;
border: 2px solid black;
left: 0;
top: 0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.2/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.13/vue.js"></script>
<div id="app">
<img id="pilt" v-if="!isHidden" v-bind:src="src[z]"></img>
</div>
It seems that you don't call draw anywhere. You may also consider draw the image into a canvas and than draw rect in this canvas. Checking if koors[3] != 0 is really risky as user may click at 0th pixel.

Looping code with a delay

I am trying to create a simple drag race animation. The car image moves across the page upon the click of a button until each reaches a certain position. The code below works with individual clicks moving the car a random distance each click, but I want one click to repeat the movement in a loop. I have tried putting it in a while loop referencing the value of x, but it doesn't work. Any ideas.
<html>
<head>
<meta charset="utf-8">
<title>Race Car</title>
</head>
<body>
<input type="button" id="move" value="race time" onClick="race()" />
<div id="car" style="position: absolute; top: 150px"><img src="car.png"></div>
<script src="https://code.jquery.com/jquery-2.1.0.js"></script>
<script>
//Get car position coordinates
x = $("#car").offset().left;
y = $("#car").offset().top;
//Move car across screen by resetting car position coordinates
var race = function() {
var delay = 1000;
setTimeout(function() {
$("#car").css({
left: x += Math.floor(Math.random() * 50),
top: y
});
x = $("#car").offset().left;
}, delay);
};
</script>
</body>
</html>
You're looking for setInterval.
setTimeout runs your function once after x milliseconds.
setInterval runs your function every x milliseconds.
Be sure to store your interval in a variable you can access (var timer = setInterval(fn, ms)) and then clear it when you want to stop the interval (clearInterval(timer)).
Why are you using variables instead of standard functions? Anyway, you can call your function recursively from within the setTimeout function until a condition is met. Try this:
<html>
<head>
<meta charset="utf-8">
<title>Race Car</title>
</head>
<body>
<input type="button" id="move" value="race time" onClick="race()" />
<div id="car" style="position: absolute; top: 150px"><img src="car.png"></div>
<script src="https://code.jquery.com/jquery-2.1.0.js"></script>
<script>
//Get car position coordinates
x = $("#car").offset().left;
y = $("#car").offset().top;
//Move car across screen by resetting car position coordinates
function race() {
var delay = 1000;
setTimeout(function() {
$("#car").css({
left: x += Math.floor(Math.random() * 50),
top: y
});
if (x < 200) //Specify where you want the movement to end.
race();
}, delay);
}
</script>
</body>
</html>
You should use recursive setTimeout() not setInterval().
See why here --> https://develoger.com/settimeout-vs-setinterval-cff85142555b
This is how I like to approach this problem, with the function bellow you can pass number of iterations, time in between and actually callback.
How many times? (you can set infinity)
For how much to delay (in seconds)
And to do WHAT?
See working example here --> https://jsfiddle.net/nmitic/bbr6y2a4/
const repeat = (numberOfIterations, timeBetweenItereation, stuffToRepeat) => {
let iterationCounter = 0;
const repeater = () => {
setTimeout( () => {
stuffToRepeat();
iterationCounter++;
if (iterationCounter >= numberOfIterations && numberOfIterations
!== Infinity) {
return;
};
if (iterationCounter >= numberOfIterations) {
return;
};
repeater();
}, 1000 * timeBetweenItereation);
};
repeater();
};
This will clear interval whenever you click the button and the race starts from the begining.
Also no ned to set the top at every call, just left is enough.
Happy racing!!!
x = $("#car").offset().left;
y = $("#car").offset().top;
//Move car across screen by resetting car position coordinates
var interval;
var race = function() {
clearInterval(interval);
var delay = 100, x=0;
interval = setInterval(function() {
$("#car").css({
left: x += Math.floor(Math.random() * 50),
});
x = $("#car").offset().left;
}, delay);
};
<input type="button" id="move" value="race time" onClick="race()" />
<div id="car" style="position: absolute; top: 150px"><img src="car.jpg"></div>

Using JavaScript Animation to Move Rectangle With Arrow Keys

I'm trying to move a rectangle in a canvas element, and haven't had any luck. This is my code so far. I want to do it in PURE JavaScript, not jQuery or any other library.
My JavaScript:
window.onload = beginningMovement; // load beginning movement
function beginningMovement() {
var elem = document.getElementById("rectangle");
var pos = 0;
var id = setInterval(frame, 8);
function frame() {
if (pos == 203) {
clearInterval(id);
//Color funcs
setTimeout(black, 0);
setTimeout(lightgray, 500);
setTimeout(black, 1000);
setTimeout(lightgray, 1500);
setTimeout(black, 2000);
setTimeout(lightgray, 2500);
function black() {
var color = document.getElementById('container').style.backgroundColor = "black";
}
function lightgray() {
var color = document.getElementById('container').style.backgroundColor = "lightgray";
}
//End of color funcs
} else {
pos++;
elem.style.top = pos + 'px';
elem.style.middle = pos + 'px';
}
}
}
//Arrow switching
var X = 40;
var Y = 20;
function switchKey(event) {
Key = event.keyCode;
myCanvas.fillstyle = "white";
myCanvas.filRect(X, Y, 50, 50);
}
switch(key) {
case 37: X -=5;
break;
case 37: Y -=5;
break;
case 37: X +=5;
break;
case 37: Y +=5;
}
myCanvas.fillstyle = "blue";
myCanvas.filRect(X, Y, 50, 50);
HTML:
<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">
#container {
width: 450px;
height: 400px;
border: 5px solid black;
background: lightgray;
}
#rectangle {
height: 50px;
width: 150px;
background: cyan;
margin-left: 150px;
margin-top: 160px;
position: absolute;
}
</style>
</head>
<body onkeyup="switchKey(event)">
<div id="container">
<div id="rectangle">
</div>
</div>
</body>
</html>
I've been at this for hours and haven't had any luck. I would appreciate it if someone could help me, and get this done soon.
There were a lot of issues with you code and I eventually thought it would be easier to remake a simple version for you to look at. If you really want to stick with your version, please check any browser console for errors and you'll see the issues I'm seeing. Some functions aren't yet available onload, there is no key variable, yet there is a Key variable. There were other issues, too, regarding variable and function scope.
Here's a new version for you to look at: http://codepen.io/antibland/pen/ONwEBE
It's not my finest work, but it's a step in the right direction for you. Instead of setInterval, you should be using requestAnimationFrame to do the redraw. It's much more performant (more on that here if you're interested).
In my demo, a real <canvas> element is used. The smaller rectangle is added to it. I figured you intended to use the real canvas since you included a myCanvas variable in your code. If not, apologies. Either way, you'll probably want to add bounds checking in there, to keep the small rectangle from moving beyond the walls.
Good luck!

Moving Pictures Bugs - JavaScript

After abt 2hrs or so, I managed to get the images moving, but i met with some issues:
1) Images move beyond the "bound" - image overlap is ok, but beyond the side of the browser
2) Moving the scroll bar to the right (0 -> 20) makes the image move closer - OK
but moving the scroll bar to the left (20 -> 0) does not return the images back to where they were
I seek you guys assistance to help debug and clean the code.
Thank You in advance :) and apologies for my weak sense of coding >.<
Images Used:
lion tail
lion head
Below is the "Draft" HTML Code:
<!DOCTYPE html>
<html>
<head>
<title>Moving Image</title>
<script src="./movingPicture.js" type="text/javascript"></script>
</head>
<body>
<form>
<img id="IMG_LEFT" src="./lion tail.gif" />
<img id="IMG_RIGHT" src="./lion head.gif" />
<p>Move the Bar to Move the Images Closer</p>
<input type="range" min="0" max="100" value="0" steps="2" oninput="moveRight(value);"/>
</form>
</body>
</html>
Below is the Javascript Code:
var imgObjLeft = null;
var imgObjRight = null;
function init()
{
imgObjLeft = document.getElementById('IMG_LEFT');
imgObjLeft.style.position= 'relative';
imgObjLeft.style.left = '0px';
imgObjRight = document.getElementById('IMG_RIGHT');
imgObjRight.style.position= 'relative';
imgObjRight.style.left = '100px';
}
function moveRight(value)
{
valueH = value/2;
imgObjLeft.style.left = parseInt(imgObjLeft.style.left) + valueH + 'px';
imgObjRight.style.left = parseInt(imgObjRight.style.left) - valueH + 'px';
}
window.onload =init;
Try the following JS. You dont seem to give a range for the movement of these images. I have added a limit which is the max value of your range control. you can increase the value by increasing the max value of your slider
var imgObjLeft = null;
var imgObjRight = null;
function init() {
imgObjLeft = document.getElementById('IMG_LEFT');
imgObjLeft.style.position = 'relative';
imgObjLeft.style.left = '0px';
imgObjRight = document.getElementById('IMG_RIGHT');
imgObjRight.style.position = 'relative';
imgObjRight.style.left = '100px';
}
function moveRight(value) {
valueH = value;
imgObjLeft.style.left = valueH + 'px';
imgObjRight.style.left = (100 - valueH) + 'px';
}
window.onload = init;

onClick event on image while already moving

I'm trying to make an image that moves to the right on my website. When you click the image, it should move to the left. For now, this doesn't happen, any ideas? If possible, in pure Javascript and no Jquery ;)
Also, if you click the image for a second time, it should move to the right again. Every consecutive time, the image should move to the other side. I guess this would be best with a for-loop?
<script type"text/javascript">
window.onload = init;
var winWidth = window.innerWidth - movingblockobject.scrollWidth; //get width of window
var movingblock = null //object
movingblock.onclick = moveBlockLeft();
function init() {
movingblock = document.getElementById('movingblockobject'); //get object
movingblock.style.left = '0px'; //initial position
moveBlockRight(); //start animiation
}
function moveBlockRight() {
if (parseInt(movingblock.style.left) < winWidth) { // stop function if element touches max window width
movingblock.style.left = parseInt(movingblock.style.left) + 10 + 'px'; //move right by 10px
setTimeout(moveBlockRight,20); //call moveBlockRight in 20 msec
} else {
return;
}
}
function moveBlockLeft () {
movingblock.style.right = parseInt(movingblock.style.right) + 10 + 'px'
}
</script>
Please first go through the Basics of javascript before trying out something .
window.onload = init;
var winWidth = window.innerWidth;
var movingblock = null;
var intervalid = null;
var isLeft = false;
function init() {
console.log('Init');
movingblock = document.getElementById('movingblockobject');
movingblock.style.left = '0px';
intervalid = setInterval(function() {
moveBlock(true);
}, 2000);
movingblock.onclick = function() {
moveBlock(false);
};
}
function moveBlock(isSetInterval) {
if (!isSetInterval)
isLeft = !isLeft;
if (parseInt(movingblock.style.left) < winWidth) {
if (isLeft)
movingblock.style.left = parseInt(movingblock.style.left) - 10 + 'px';
else
movingblock.style.left = parseInt(movingblock.style.left) + 10 + 'px';
} else {
movingblock.style.left = '0px';
}
}
function moveBlockLeft() {
clearInterval(intervalid);
movingblock.style.right = parseInt(movingblock.style.right) + 10 + 'px';
intervalid = setInterval(moveBlockRight, 20);
}
<div id="movingblockobject" style="width:50px;height:50px;border:solid;position: absolute;">
var movingblock = null
this are mistakes you have delclared it as null and next line you are binding click event.Also for assigning click event you should assign the name of the function.
movingblock.onclick = moveBlockLeft;
above is basic bug there are lot like the above.I feel Self learning is much better for basics
try yourself.
try the above it will work but please try yourself
You should CSS3 transition, works like a charm. Just toggle a class "right"
HTML:
<html>
<head>
<style>
#movingblockobject{
width: 40px;
padding: 10px;
position: fixed;
height:10px;
left:0px;
-webkit-transition: left 2s; /* For Safari 3.1 to 6.0 */
transition: left 2s;
}
#movingblockobject.right{
left:200px;
}
</style>
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script>
$( document ).ready(function() {
$('#movingblockobject').on('click', function (e) {
$('#movingblockobject').toggleClass("right");
});
});
</script>
</head>
<body>
<div id="movingblockobject" class="demo">add image here</div>
</body>
</html>

Categories