I am trying to make a 3D like effect in HTML Canvas. I want to have the effect of a 2D object coming towards the first person view. Is there a way to make it look like a rectangle is moving on the Z axis?
This is my code. I sort of have what I want but it does not really look natural.
Use the up arrow key to move the object. You can also use the left and right arrow keys to move it to the left and right.
I am trying to make a game similar to this game: http://game.notch.net/christmaschopping/
var canvas = document.getElementById('canv');
var c = canvas.getContext('2d');
window.onload = function() {
start();
setInterval(update, 10);
}
class Obstacle {
constructor(x, y) {
this.x = x;
this.y = y;
this.w = 30;
this.h = 60;
this.xSpeed = 0;
this.ySpeed = 0;
this.passedView = false;
this.zSpeed = 0;
}
show() {
if (!this.passedView) {
c.fillStyle = 'green';
c.fillRect(this.x, this.y, this.w, this.h);
}
}
update() {
this.x += this.xSpeed;
this.y += this.ySpeed;
this.w+=this.zSpeed;
this.h+=this.zSpeed;
this.x-=this.zSpeed/2;
this.y-=this.zSpeed/2;
}
}
var ob;
function start() {
ob = new Obstacle(385, 370);
}
function update() {
//background
c.fillStyle = 'lightblue';
c.fillRect(0, 0, 800, 800);
//obstacle
ob.show();
ob.update();
}
function keyDown(e) {
switch(e.keyCode) {
case 39:
ob.xSpeed = 5;
break;
case 37:
ob.xSpeed = -5;
break;
case 38:
ob.zSpeed = 5;
break;
}
}
function keyUp(e) {
switch(e.keyCode) {
case 39:
ob.xSpeed = 0;
break;
case 37:
ob.xSpeed = 0;
break;
case 38:
ob.zSpeed = 0;
break;
}
}
document.onkeydown = keyDown;
document.onkeyup = keyUp;
<canvas id='canv' width=800 height=800></canvas>
<script src="main.js"></script>
its hard to perceive 3D looking a one object so give yourself several objects moving independently ... let the objects move on their own
I suggest you introduce the notion of perspective projection ... its why objects moving away get smaller
to keep it simple assign objects a size then scale that size using their Z dimension location
in addition to giving each object a location in 3d ( x,y,z ) put all of them inside a world say the inside of a cube or a sphere
launch each new object in some random direction at a random speed, random size
put all objects into some datastructure so you have an array you can iterate across to do things to each object
introduce the notion of time into this world ... iterate across the datastructure of your objects and perform actions as in
gently move the x,y,z location of the current object just a tiny bit in each dim ... keep tract of the sign ( pos/neg ) of each increment in each dimension
if the current x or y or z goes outside the interior of the cube or sphere then toggle the sign of the increment for that dimension as mentioned above
when you draw a given object scale its size based on its Z dimension ... this is perspective projection
object_rendered_size = object_constant_size * Z dimension for this object
advance the clock to the next epoch ... put above loop inside an outer loop so everything keeps moving all the time
And yes this will give you 3D ... no need to jump straight into WebGL until after projects like this just code themselves ... with today's hardware you can go very far using your current approach of using canvas ... teach yourself linear algebra to manipulate object or camera location using matrix mechanics
have Fun !!!
Related
I'm new in JavaScript and I'm creating a kind of shooting game. I'm to make 1 object to move toward another object. So the "bullet" get the location of the "prey" and it will move toward it. I have no idea how to implement that, I can't find anything similar on the internet. So, I tried at first something simpler:
In the following code the "bullet" move to the left. How can I specify to move it toward an object?
I have 2 object. It's the enemyBullet object(not the bullet object) which should go toward another object.
PS: English is not my native language. Sorry for any confusion
thanks.
this.draw = function () {
this.context.clearRect(this.x + 2, this.y + 1.5, this.width - 4.5, this.height);
//x+ the speed make it go to the left
this.x += this.speed;
if (self === "bullet" && this.x >= this.canvasWidth) {
return true;
}
else if (self === "enemyBullet" && this.x >= 1000) {
console.log(this.x);
return true;
}
else {
if (self === "bullet") {
this.context.drawImage(imageRepository.bullet, this.x, this.y);
}
else if (self === "enemyBullet") {
this.context.drawImage(imageRepository.enemyBullet, this.x, this.y);
}
return false;
}
};
Normalised vector
You need to find the normalised vector from one object to the next. A vector is just an arrow that has a direction and a length. In this case we normalise the length, that is make it equal to 1 unit long. We do this so we can easily set a speed when using the vector.
Function to return a vector from one point to the next
// Code is in ES6
// fx, fy is from coordinate
// tx, ty is to coordinate
function getNormVec(fx, fy, tx, ty){
var x = tx - fx; // get differance
var y = ty - fy;
var dist = Math.sqrt(x * x + y * y); // get the distance.
x /= dist; // normalised difference
y /= dist;
return {x,y};
}
Once you have the vector you can move an object by adding the vector times the speed. Example of creating and moving a bullet from myObj to myTarget
var myObj = {}
var myTarget = {};
var myBullet = {}
myObj.x = 100;
myObj.y = 100;
myTarget.x = 1000
myTarget.y = 1000
var vecToTag = getNormVect(myObj.x, myObj.y, myTarget.x, myTarget.y);
myBullet.nx = vecToTag.x; // set bullets direction vector
myBullet.ny = vecToTag.y;
myBullet.x = myObj.x; // set the bullet start position.
myBullet.y = myObj.y;
myBullet.speed = 5; // set speed 5 pixels per frame
To move the bullet
myBullet.x += myBullet.nx * myBullet.speed;
myBullet.y += myBullet.ny * myBullet.speed;
I've made some pacman-like shapes that are animated on an html5 canvas and currently only move back and forth in 1 dimension. The program currently has a feature for directional buttons that change the path of the shapes, but I need the shapes to simply follow the cursor coordinates and not just travel in one direction. Currently, I'm getting the coordinates of the mouse using this:
function readMouseMove(e) {
var result_x = document.getElement('x_result');
var result_y = document.getElement('y_result');
result_x.innerHTML = e.clientX;
result_y.innerHTML = e.clientY;
}
document.onmousemove = readMouseMove;
var xR = document.getElementById("x_result");
var yR = document.getElementById("y_result");
var x = xR.innerHTML
var y = yR.innerHTML
As I'm having to make elements and extract the innerHTML of the cursor coordinates, I don't think this is the most efficient way to do this. I have a function object for each pacman shape that has attributes of its x and y positions (which shift based on the direction input), and the size and speed so I'm looking for a way to continuously set the x and y attributes for each object inside of my animationLoop. I don't really want to use jquery as is done here, as I'm using canvas transformations so what would be the best way to go about doing this? I have the code for the animation loop below for reference, thanks:
function animationLoop() {
canvas.width = canvas.width;
renderGrid(20, "red");
for (var i = 0; i < WokkaWokkas.length; i++) {
//WWM is the name of each pacman object
var WWM = WokkaWokkas[i];
renderContent(WWM);
setAngles(WWM);
//used for the direction input
switch (WWM.direction) {
case up:
WWM.posY -= WWM.speed;
if (WWM.posY <= 0) WWM.direction = down;
break;
case down:
WWM.posY += WWM.speed;
if (WWM.posY > 600) WWM.direction = up;
break;
case left:
WWM.posX -= WWM.speed;
if (WWM.posX < 0) WWM.direction = right;
break;
case right:
WWM.posX += WWM.speed;
if (WWM.posX > 600) WWM.direction = left;
break;
}
}
I'm a noob learning to work in JavaScript & HTML5 Canvas and I'm working on my first 2D game.
Thank you in advance for any tips you can provide me with.
For some reason after I change the enemy constructor function to accept object dimensions as parameters, the dimensions of Enemy are not passed to the collision detection function. This results in my Player object not being affected when crossing paths with the Enemy object.
Here is my Enemy constructor function:
// Create subclass Enemy
function Enemy(x, y, speed, leftSide, rightSide, top, bottom) {
Sprite.call(this, x, y);
this.speed = speed;
// Set variables for enemy dimensions
this.leftSide = leftSide + this.x;
this.rightSide = rightSide + this.x;
this.top = top + this.y;
this.bottom = bottom + this.y;
this.sprite = 'images/enemy-bug.png';
}
Here are the arguments I pass through it:
var allEnemies = [
new Enemy(0, 225, 60, 0, 70, 0, 75)
];
Here's my collision detection function:
function checkCollisions () {
allEnemies.forEach(function(enemy) {
if(enemy.leftSide < player.rightSide &&
enemy.rightSide > player.leftSide &&
enemy.top < player.bottom &&
enemy.bottom > player.top) {
console.log('collision!');
//Reset player position
player.startOver();
}
});
}
How can I get my collision detection function to recognise the dimension values? My player is walking around with impunity =(
Again, many thanks for any assistance you can provide and I do apologise if my post doesn't conform to question standards. This is my first post on StackOverflow.
I want to style the second elment in this array by adding a CSS Property
here is a global variable to define the array
<canvas id="canvas"></canvas>
<script>
var paddles = [2], // Array containing two paddles
function update() {
// Update scores
updateScore();
// Move the paddles on mouse move
// Here we will add another condition to move the upper paddle in Y-Axis
if(mouse.x && mouse.y) {
for(var i = 1; i < paddles.length; i++) {
p = paddles[i];
// the botoom paddle
if (i ==1){
p.x = mouse.x - p.w/2;
}else{
// the top paddle
//paddles[2].x = mouse.x - p.w/2;
debugger
paddles[2].style.backgroundColor="red";
}
}
}
and here what the style I want
paddles[2].style.backgroundColor="red";
when I use the debugger I face this problem
TypeError: paddles[2].style is undefined
UPDATE:
Since it looks like you are creating some kind of "pong" or "breakout" game, I decided to take my first stab at HTML canvas and do it myself for fun. Here is a simple version that shows how to:
draw two paddles on a canvas (original author)
keep track of the "boxes" for the paddles in an array (original author)
use a loop via setInterval to redraw the canvas as it gets updated (original author)
use the keyboard to move shapes around on HTML canvas (my code)
See the fiddle for working demo and full code: http://jsfiddle.net/z4ckpcLc/1/
I will not post the full code because I didn't write most of it.. I used the example from this site for the code for drawing the boxes and for keeping track of them in an array: http://simonsarris.com/project/canvasdemo/demo1.html
The function I added to this example is the arrowKeyMove() handler, wired up to the onkeydown event of document.body via this line: document.body.onkeydown = arrowKeyMove;
function arrowKeyMove(e) {
var direction = 0; // -1 means left, 1 means right, 0 means no change
var moveFactor = 10; // higher factor = more movement when arrow keys pressed
var arrowKeyUsed = false; // to indicate which 'paddle' we are moving
switch (e.which) {
case 37:
// left arrow (upper paddle)
direction = -1;
arrowKeyUsed = true;
break;
case 39:
// right arrow (upper paddle)
direction = 1;
arrowKeyUsed = true;
break;
case 65:
// "a" key for left strafe (lower paddle)
direction = -1;
break;
case 68:
// "d" key for right strafe (lower paddle)
direction = 1;
break;
}
var boxIndex = 1; // box index defaults to lower paddle
if (arrowKeyUsed) { // if using arrow keys, we are moving upper paddle
boxIndex = 0;
}
var maxX = 240; // constrain movement to within 10px of box borders (240 == canvas width minus paddle width minus padding)
var minX = 20;
var box = boxes[boxIndex]; // grab the box; we will update position and redraw canvas
if((direction < 0 && box.x >= minX) || (direction > 0 && box.x <= maxX))
{
// move the box in the desired direction multiplied by moveFactor
box.x = box.x + (direction * moveFactor);
invalidate(); // invalidate canvas since graphic elements changed
}
}
ORIGINAL ANSWER:
Array items use zero-based indexing.
If you only have two paddles like you said, you must use index 1, not 2. And if you want to access the first paddle, use 0, not 1. You probably want your for loop to use var i=0 instead, and basically change places you are checking 1 to 0.
For example:
paddles[0].style.backgroundColor="red"; // paddle 1
paddles[1].style.backgroundColor="red"; // paddle 2
Also, var array = [2] does not create a two-array element. It creates a one-array element with an integer value of 2
For DOM elements you may want something like this:
<div id='paddle1'></div>
<div id='paddle2'></div>
<script type='text/javascript'>
var paddles = [];
paddles[0] = document.getElementById('paddle1');
paddles[1] = document.getElementById('paddle2');
paddles[0].style.backgroundColor="red"; // paddle 1 is red
paddles[1].style.backgroundColor="orange"; // paddle 2 is orange
</script>
I'm not sure, but maybe you can use something like this:
paddles[2].css('background-color','red');
edit: now I see you don't use jQuery, so my solution wouldn't work
I try to make an animation of character by reading this tutorial:
http://mrbool.com/html5-canvas-moving-a-character-with-sprites/26239 .
It's quite ease to make the character go left ('go right' is already done). But how to make the character jump (with animation)?
I was thinking about something like this:
case 38:
if (y + dy > HEIGHT){
y += dy
}
break;
...but it just move character up (without animation). Can someone help me? Some code example will be useful.
You get the jumping behavior like this (using the same code on the tutorial)
JSFiddle
var canvas;// the canvas element which will draw on
var ctx;// the "context" of the canvas that will be used (2D or 3D)
var dx = 50;// the rate of change (speed) horizontal object
var x = 30;// horizontal position of the object (with initial value)
var y = 150;// vertical position of the object (with initial value)
var limit = 10; //jump limit
var jump_y = y;
var WIDTH = 1000;// width of the rectangular area
var HEIGHT = 340;// height of the rectangular area
var tile1 = new Image ();// Image to be loaded and drawn on canvas
var posicao = 0;// display the current position of the character
var NUM_POSICOES = 6;// Number of images that make up the movement
var goingDown = false;
var jumping;
function KeyDown(evt){
switch (evt.keyCode) {
case 39: /* Arrow to the right */
if (x + dx < WIDTH){
x += dx;
posicao++;
if(posicao == NUM_POSICOES)
posicao = 1;
Update();
}
break;
case 38:
jumping = setInterval(Jump, 100);
}
}
function Draw() {
ctx.font="20px Georgia";
ctx.beginPath();
ctx.fillStyle = "red";
ctx.beginPath();
ctx.rect(x, y, 10, 10);
ctx.closePath();
ctx.fill();
console.log(posicao);
}
function LimparTela() {
ctx.fillStyle = "rgb(233,233,233)";
ctx.beginPath();
ctx.rect(0, 0, WIDTH, HEIGHT);
ctx.closePath();
ctx.fill();
}
function Update() {
LimparTela();
Draw();
}
var Jump = function(){
if(y > limit && !goingDown){
y-=10;
console.log('jumping: ' + y);
} else{
goingDown = true;
y +=10;
if(y > jump_y){
clearInterval(jumping);
goingDown = false;
}
}
}
function Start() {
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
return setInterval(Update, 100);
}
window.addEventListener('keydown', KeyDown);
Start();
There's no one right answer to this question, and unless you find a game-design library, there's no simple one, either. Your problem is that you're moving the character instantaneously in response to input, but a jump requires movement over time. You'll have to either find a moving sprites library - I don't have one in particular to recommend, but I'm sure Google has several - or set up something yourself that runs every so many milliseconds and updates the character's position and some sort of velocity variable.
Edit: Looking at that tutorial, the simplest solution that comes to mind is to put your animation code inside of Update(), like so:
function Update() {
LimparTela();
Animate();
Draw();
}
Inside of Animate(), you should keep track of the character's height and vertical momentum. If the momentum is positive, increase the y position a little, otherwise decrease it a little. Either way, reduce momentum a bit. Add something to keep the character from going through the floor, and have the up key set the character's momentum to be positive if he's on the floor.
Note that this is an incredibly bare-bones solution, but for a basic tutorial it'll do the job.