I've been trying to put together a simple maze game using HTML5 and Javascript. I can successfully load the HTML and CSS content onto the page, but no matter what I try, I can't get the JS to load. It's definitely saved as a .html file and i've only been using Sublime text to put it together (but I wouldn't have thought that would have an affect anyway). Just a bit stumped really, so I thought it must be something I've missed in my code. I wasn't sure if I've missed something?
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title> Maze Game </title>
</head>
<style>
canvas {
border: 8px double navy;
background: white;
}
img {
display: none;
}
button {
padding: 3px;
}
</style>
<body>
<canvas id="canvas"> </canvas>
<img id="sprite" src="sprite.png">
<script>
//these define the global variables for the canvas and the drawing context
var canvas;
var context;
var x = 0;
var y = 0; //positioning of the sprite
var dx = 0;
var dy = 0; //momentum of the sprite at start
window.onload = function() {
//setting up the canvas
canvas = document.getElementById("canvas");
context = canvas.getContext("2d");
//Draws the maze background
drawMaze("maze.png", 268, 5);
//On key press, run the following function
window.onkeydown = processKey;
};
var x = 0;
var y = 0;
function drawMaze(mazeFile, Xstart, Ystart) {
//This loads the maze picture in
dx = 0;
dy = 0; //if the face is already moving, stop it
var imgMaze = new Image();
imgMaze.onLoad = function() {
canvas.width = imgMaze.width;
canvas.height = imgMaze.height;
//Draws the maze onto the canvas
context.drawImage(imgMaze, 0, 0);
//draws the sprite and positions
x = Xstart;
y = Ystart;
var imgSprite = document.getElementById("sprite");
context.drawImage(imgSprite, x, y);
context.stroke();
//sets a short timer for the next frame to be drawn in (10ms)
setTimeout("drawFrame()", 10);
};
imgMaze.src = mazeFile;
}
function processKey(e) { //e needs to be used for event handling
//stop the sprite if it's already moving - enables collision
var dx = 0;
var dy = 0;
//condition for the Up arrow being pressed
if (e.keyCode == 38) {
dy = -1;
}
//condition for the Left arrow being pressed
if (e.keyCode == 37) {
dx = -1;
}
//condition for the Down arrow being pressed
if (e.keyCode == 40) {
dy = 1;
}
//condition for the Right arrow being pressed
if (e.keyCode == 39) {
dx = 1;
}
}
function drawFrame() {
if (dx != 0 || dy != 0) {
context.beginPath();
context.fillStyle = "rgb(254,244,207)";
context.rect(x, y, 15, 15);
context.fill
x += dx;
y += dy;
if (checkForCollision()) {
(dx/y = 0)
x -= dx;
y -= dy;
dx = 0;
dy = 0;
}
//Now we can finally draw the sprite!
var imgSprite = document.getElementById("sprite");
context.drawImage(imgSprite, x, y);
if (y > (canvas.height - 17)) {
alert("Congratulations! You made it!");
return;
}
}
timer = setTimeout(drawFrame, 10);
}
var imageData = context.getImageData(0, 0, 100, 50);
var pixels = imageData.data;
for (var i = 0, n = pixels.length; i < n; i += 4) {
//This will get the data/values for one pixel
var red = pixels[i];
var green = pixels [i+1];
var blue = pixels [i+2];
var alpha = pixels [i+3];
//This will invert the colours
pixels[i] = 255 - red;
pixels[i+1] = 255 - green;
pixels[i+2] = 255 - blue;
}
context.putImageData(imageData, 0, 0);
function checkForCollision() {
var imgData = context.getImageData(x-1, y-1, 15+2, 15+2);
var pixels = imgData.data;
//Then we need to perform a check, same as above
for (var i = 0; n = pixels.length, i < n; i += 4) {
var red = pixels[i];
var green = pixels[i+1];
var blue = pixels[i+2];
var alpha = pixels[i+3];
//now check for the black pixels for a wall
if (red == 0 && green == 0 && blue == 0) {
return true;
} //checks for a greyish colour - possibly the edge of a wall
if (red == 169 && green == 169 && blue == 169) {
return true;
}
}
return false; //there was no collision
}
</script>
</body>
</html>
There are lots of errors in this code. For example, in this section alone:
(commented where some issues are)
function drawFrame() {
if (dx != 0 || dy != 0) {
context.beginPath();
context.fillStyle = "rgb(254,244,207)";
context.rect(x, y, 15, 15);
context.fill // Missing parentheses and semicolon
x += dx;
y += dy;
if (checkForCollision()) {
(dx/y = 0) // Equivalent to { dx / (y = 0) }
x -= dx; // which both serves no purpose and divides by zero
y -= dy;
dx = 0;
dy = 0;
}
//Now we can finally draw the sprite!
var imgSprite = document.getElementById("sprite");
context.drawImage(imgSprite, x, y);
if (y > (canvas.height - 17)) {
alert("Congratulations! You made it!");
return;
}
}
timer = setTimeout(drawFrame, 10); // timer is not defined anywhere
// also you are calling the function within itself
// with no end condition, so it's an infinite loop
}
And on line 48:
setTimeout("drawFrame()", 10);
You should be passing the function as just the function identifier, not as a string. As such:
setTimeout(drawFrame, 10);
These are just a few. There are also some logical errors, variables that are defined but never used, and more. In its current state this will not compile.
Also, if only just for clarity's sake, try to define a script type instead of just leaving the tag empty, like:
<script type="text/javascript">
// Some JS
</script>
It can be hard when you've been staring at code for hours, but it helps to give each section a slow read-through and think about exactly what the code is doing. You can avoid lots of syntactical and logical errors this way.
I also recommend using a text editor or online interface (JS Bin, JSfiddle, etc.) that has JShint/lint or some kind of code-checking functionality. You can even use http://www.javascriptlint.com/online_lint.php and paste your whole code in there.
Related
Catbus game
I'm trying to make my cat only jump once until it lands back on the ground. I've tried to add a statement that makes it only go to a certain point, but the velocity keeps working against it. It begins to act like a basketball that has been bounced to much. I wouldn't want to add a collider (even though I have debated it). It would just make it worse...
The code is as follows:
let img; //background
var bgImg; //also the background
var x1 = 0;
var x2;
var scrollSpeed = 4; //how fast background is
let bing; //for music
let cat;
var mode; //determines whether the game has started
let gravity = 0.2; //jumping forces
let velocity = 0.1;
let upForce = 6;
let startY = 730; //where cat bus jumps from
let startX = 70;
var font1; //custom fonts
var font2;
p5.disableFriendlyErrors = true; //avoids errors
function preload() {
bgImg = loadImage("backgwound.png"); //importing background
bing = loadSound("catbus theme song.mp3"); //importing music
font1 = loadFont("Big Font.TTF");
font2 = loadFont("Smaller Font.ttf");
}
function setup() {
createCanvas(1000, 1000); //canvas size
img = loadImage("backgwound.png"); //background in
x2 = width;
bing.loop(); //loops the music
cat = {
//coordinates for catbus
x: startX,
y: startY,
};
catGif = createImg("catgif.gif"); //creates catbus
catGif.position(cat.x, cat.y); //creates position
catGif.size(270, 100); //creates how big
mode = 0; //game start
textSize(50); //text size
}
function draw() {
let time = frameCount; //start background loop
image(img, 0 - time, 0);
image(bgImg, x1, 2, width, height);
image(bgImg, x2, 2, width, height);
x1 -= scrollSpeed;
x2 -= scrollSpeed;
if (x1 <= -width) {
x1 = width;
}
if (x2 <= -width) {
x2 = width;
} //end background loop
fill("white"); //text colour
if (mode == 0) {
textSize(20);
textFont(font1);
text("press SPACE to start the game!", 240, 500); //what text to type
}
if (mode == 0) {
textSize(35);
textFont(font2);
text("CATBUS BIZZARE ADVENTURE", 90, 450); //what text to type
}
cat.y = cat.y + velocity; //code for jumping
velocity = velocity + gravity;
if (cat.y > startY) {
velocity = 0;
// cat.y = startY;
}
catGif.position(cat.x, cat.y);
}
function keyPressed() {
if (keyCode === 32) { //spacebar code
// if ((cat.y = 730)) {
// cat.y > 730;
mode = 1;
velocity += -upForce;
}
// }
}
You can simply do this to the keyPressed function
function keyPressed() {
if (keyCode === 32 && velocity == 0) { //spacebar code
// if ((cat.y = 730)) {
// cat.y > 730;
mode = 1;
velocity += -upForce;
}
}
I'm still getting used to objects, but no matter what I do I can't get my objects to reverse their x or y. It doesn't even register that any of them have hit a canvas wall, they just go off screen.
Here's my code:
"use strict";
//variables
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var randomX = 0; //in for loop use Math.random()
var randomY = 0; //in for loop use Math.random()
var ballRadius = 10;
var ballX = canvas.width/2;
var ballY = canvas.height-30;
var ballMove = [];
var ballObject = [];
//declares the objects x and y's
for(var i = 0; i < 10; i++){
ballObject[i] = { x: 0, y: 0 };
ballMove[i] = { x:1, y:-1 };
randomX = (Math.random() * (canvas.width-ballRadius)) +1;
randomY = (Math.random() * (canvas.height-ballRadius)) +1;
ballObject[i].x = randomX;
ballObject[i].y = randomY;
}
function draw()
{
ctx.clearRect(0,0,canvas.width, canvas.height);
//didn't actually need this for loop but was experimenting
for(var i = 0; i<10; i++){
drawBall(i);
}
moveBall();
//this method should check the x and y of the balls.
bounceBall();
}
// basic ball drawing function
function drawBall(i)
{
ctx.beginPath();
ctx.arc(ballObject[i].x, ballObject[i].y, ballRadius, 0, Math.PI*2);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
//extremely basic move function (just adds to x and y)
function moveBall()
{
for(var i = 0; i < 10; i++){
ballObject[i].x += ballMove[i].x;
ballObject[i].y += ballMove[i].y;
}
}
//WHY IT NO WORK
//for whatever reason this function doesn't reverse the x and y
function bounceBall()
{
for(var i = 0; i < 10; i++)
{
if (ballObject[i].x == canvas.width-ballRadius || ballObject[i].x == 0+ballRadius){
alert("i've hit a wall");
ballMove[i].x = -ballMove[i].x;
}
if (ballObject[i].y == canvas.height-ballRadius || ballObject[i].y == 0+ballRadius){
alert("i've hit a wall");
ballMove[i].y = -ballMove[i].y;
}
}
}
setInterval(draw, 10);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Gamedev Canvas Workshop</title>
<style>
* { padding: 0; margin: 15; }
canvas { background: #eee; display: block; margin: 0 auto; }
</style>
</head>
<body>
<canvas id="myCanvas" width="480" height="320"></canvas>
<script src="ballPop.js">
</script>
</body>
</html>
I've kinda made a mess of my code trying to use objects and make them bounce.
I made a brick break game after following some tut online and the bounce worked fine - however that same concept doesn't seem to work with objects.
This likely has to do with the fact that your ball coordinates are floating point values. For example:
> (Math.random() * (200 - 3)) + 1
179.1025927176495
When you do your checks in bounceBall you may get better results by checking if the ball would exceed the boundary
function bounceBall()
{
for(var i = 0; i < 10; i++)
{
if (ballObject[i].x >= canvas.width-ballRadius || ballObject[i].x <= 0+ballRadius) {
alert("i've hit a wall");
ballMove[i].x = -ballMove[i].x;
}
if (ballObject[i].y >= canvas.height-ballRadius || ballObject[i].y <= 0+ballRadius){
alert("i've hit a wall");
ballMove[i].y = -ballMove[i].y;
}
}
}
Note: == changed to >= and <=
I am looking for a "random-dot kinematograms" implementation in javascript/HTML (preferably in ReactJS) that I can use in web-based experiments.
Basically, dots moving (arrows indicate motion direction) within a visual field (the circle canvas). Signal dots (shown as filled circles) all move in the same direction, while the noise dots can move in random directions. In the actual display, the correlated and uncorrelated dots in a frame appear the same; the filled and open dots are used in the figure only to explain the principle.
Where can I find an implementation for something like this, where the user can specify the direction using a mouse or slider? Or how would one approach implementing this task in ReactJS? (new to javascript, so tips would be highly appreciated).
I've build you simple canvas based kinematogram that you should extend to your needs. What I've done so far is:
added bucket of balls (total:100) = 50 black, 50 white
blacks are going in strait direction
whites are going randomly and they also bounce from walls
whites are going in random speed
blacks are going in constant speed
To adapt blacks directions you could start by looking in this answer to determine the mouse direction and patch my balls.push loop for that.
To be able to vary balls ratio, I'd add input field somewhere on page and replace my hardcoded noise variable.. something like:
<input type="text" id="t" />
and in javascript pick it like:
var t = document.getElementById("t");
t.addEventListener('keyup', function(ev){ /* update value */ }, false);
hope this helps you in your research and I do encourage you to take a look into the code that I'm posting so try to learn it and extend it :)
;(function() {
'use strict';
var c = document.getElementById('c');
var t = document.getElementById('t');
var ctx = c.getContext('2d');
var w = c.width = window.innerWidth;
var h = c.height = window.innerHeight;
// current dots
var balls=[];
var total = 100;
var noise = 50; // here we could pick the value from user input
var bounce = -1;
for(var i=0 ; i<total ; i++){
balls.push({
x : Math.random() * w,
y : Math.random() * h,
vx : ( i < noise ) ? (Math.random() * (2.5 - 1 + 1) + 1) : 2,
vy : ( i < noise ) ? (Math.random() * (2.5 - 1 + 1) + 1) : 2,
})
}
// draw all balls each frame
function draw() {
ctx.clearRect(0, 0, w, h);
var j, dot;
for(j = 0; j < total; j++) {
dot = balls[j];
ctx.beginPath();
ctx.arc(dot.x, dot.y, 4, 0, Math.PI * 2, false);
ctx.fillStyle = (j > noise) ? "rgb(0,0,0)" : "#fff";
ctx.fill();
ctx.strokeStyle = 'black';
(j < noise) ? ctx.stroke() : '';
}
}
// movement function
function update(){
var i,dot;
for( i=0 ; i< total ; i++){
dot = balls[i];
dot.x += dot.vx;
dot.y += dot.vy;
// if ball is white, bounce it
if( i < noise){
if(dot.x > w){
dot.x = w;
dot.vx *= bounce;
}else if(dot.x <0){
dot.x = 0;
dot.vx *= bounce;
}
if(dot.y > h){
dot.y = h;
dot.vy *= bounce;
}else if(dot.y<0){
dot.y = 0;
dot.vy *= bounce;
}
// if ball is black do not bounce
} else {
if(dot.x > w){
dot.x = 0;
}else if(dot.x <0){
dot.x = w;
}
if(dot.y > h){
dot.y = 0;
}else if(dot.y<0){
dot.y = h;
}
}
}
}
// loop the animation
requestAnimationFrame(function loop() {
requestAnimationFrame(loop);
update();
draw();
});
})();
html,
body {
padding: 0;
margin: 0;
}
canvas {
display: block;
background: white;
}
<canvas id="c"></canvas>
In my on-the-fly editor tool I would really appreciate to get actual rendered height of the text / font - (I do not mean just getting CSS font-size, neither computed nor preset).
Is this achieveable in javascript?
If not directly, is possible something as rendering font in canvas the same way as it is rendered as regular text - and then finding out?
EDIT - my "dev" solution: Based on suggested links I've built a little pure-javascript code, that goes through pixels in canvas and analyses whether the pixel is white or not and acts accordingly, it is hardly a developer version of a code - just outputs few useful info and shows how to access computed data - http://jsfiddle.net/DV9Bw/1325/
HTML:
<canvas id="exampleSomePrettyRandomness" width="200" height="60"></canvas>
<div id="statusSomePrettyRandomness"></div>
JS:
function findPos(obj) {
var curleft = 0, curtop = 0;
if (obj.offsetParent) {
do {
curleft += obj.offsetLeft;
curtop += obj.offsetTop;
} while (obj = obj.offsetParent);
return { x: curleft, y: curtop };
}
return undefined;
}
var status = document.getElementById('statusSomePrettyRandomness');
var example = document.getElementById('exampleSomePrettyRandomness');
var context = example.getContext('2d');
context.fillStyle = "rgb(255,255,255)";
context.fillRect(0, 0, 200, 200);
context.fillStyle = "rgb(0,0,0)";
context.font = "30px Arial";
context.fillText("Hello World",0,30);
var pos = findPos(example);
var x = example.pageX - pos.x;
var y = example.pageY - pos.y;
var foundTop = false;
xPos = 0;
yPos = 0;
topY = -1;
bottomY = -1;
var fuse = 1000;
while( fuse-- > 0 ){
//status.innerHTML += yPos+"<br>";
if( yPos == (example.offsetHeight - 2) ){
xPos++;
yPos = 0;
continue;
}
var data = context.getImageData(xPos, yPos, 1, 1).data;
if( ! foundTop ){
if( (data[0] != 255) && (data[1] != 255) && (data[2] != 255) ){
topY = yPos;
status.innerHTML += "<br>Found top: "+topY+" X:"+xPos+" Color: rgba("+data[0]+","+data[1]+","+data[2]+")"+"<br>";
foundTop = true;
}
} else {
if( (data[0] == 255) && (data[1] == 255) && (data[2] == 255) ){
bottomY = yPos;
status.innerHTML += "<br>Found bottom: "+bottomY+" X:"+xPos+"<br>";
break;
}
}
yPos++;
if( yPos > example.offsetHeight ){
status.innerHTML += ""
+"Y overflow ("+yPos+">"+example.offsetHeight+")"
+" - moving X to "+xPos
+" - reseting Y to "+yPos
+"<br>"
;
xPos++;
yPos = 0;
}
}
status.innerHTML += "Fuse:"+fuse+", Top:"+topY+", Bottom: "+bottomY+"<br>";
status.innerHTML += "Font height should be: "+(bottomY-topY)+"<br>";
EDIT 2: Why this is not a duplicate: My question is about really just real rendered height of a font or a letter, "possible duplicate" is about how much space do you need to print a text, answers provided there don't answer my exact problem anyways.
I am not aware of any method that would return the height of a text such as measureText (which does currently return the width).
However, in theory you can simply draw your text in the canvas then trim the surrounding transparent pixels then measure the canvas height..
Here is an example (the height will be logged in the console):
// Create a blank canvas (by not filling a background color).
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
// Fill it with some coloured text.. (black is default)
ctx.font = "48px serif";
ctx.textBaseline = "hanging";
ctx.fillText("Hello world", 0, 0);
// Remove the surrounding transparent pixels
// result is an actual canvas element
var result = trim(canvas);
// you could query it's width, draw it, etc..
document.body.appendChild(result);
// get the height of the trimmed area
console.log(result.height);
// Trim Canvas Pixels Method
// https://gist.github.com/remy/784508
function trim(c) {
var ctx = c.getContext('2d'),
// create a temporary canvas in which we will draw back the trimmed text
copy = document.createElement('canvas').getContext('2d'),
// Use the Canvas Image Data API, in order to get all the
// underlying pixels data of that canvas. This will basically
// return an array (Uint8ClampedArray) containing the data in the
// RGBA order. Every 4 items represent one pixel.
pixels = ctx.getImageData(0, 0, c.width, c.height),
// total pixels
l = pixels.data.length,
// main loop counter and pixels coordinates
i, x, y,
// an object that will store the area that isn't transparent
bound = { top: null, left: null, right: null, bottom: null };
// for every pixel in there
for (i = 0; i < l; i += 4) {
// if the alpha value isn't ZERO (transparent pixel)
if (pixels.data[i+3] !== 0) {
// find it's coordinates
x = (i / 4) % c.width;
y = ~~((i / 4) / c.width);
// store/update those coordinates
// inside our bounding box Object
if (bound.top === null) {
bound.top = y;
}
if (bound.left === null) {
bound.left = x;
} else if (x < bound.left) {
bound.left = x;
}
if (bound.right === null) {
bound.right = x;
} else if (bound.right < x) {
bound.right = x;
}
if (bound.bottom === null) {
bound.bottom = y;
} else if (bound.bottom < y) {
bound.bottom = y;
}
}
}
// actual height and width of the text
// (the zone that is actually filled with pixels)
var trimHeight = bound.bottom - bound.top,
trimWidth = bound.right - bound.left,
// get the zone (trimWidth x trimHeight) as an ImageData
// (Uint8ClampedArray of pixels) from our canvas
trimmed = ctx.getImageData(bound.left, bound.top, trimWidth, trimHeight);
// Draw back the ImageData into the canvas
copy.canvas.width = trimWidth;
copy.canvas.height = trimHeight;
copy.putImageData(trimmed, 0, 0);
// return the canvas element
return copy.canvas;
}
<canvas id="canvas"></canvas>
Image Data API: https://developer.mozilla.org/en-US/docs/Web/API/ImageData
I'm fairly new to Javascript and HTML5, and I'm trying to figure out how to zoom on a canvas. Let's say my Javascript code looks like this:
window.addEventListener('load', function() {
var theCanvas = document.getElementById('myCanvas');
theCanvas.style.border = "black 1px solid";
if(theCanvas && theCanvas.getContext) {
var context = theCanvas.getContext('2d');
if(context) {
var x = 10;
var y = 10;
var z = 255;
var color = "rgb(0," + z + ",0)";
context.fillStyle = "rgb(100,0,0)";
for(var y = 0; y <= 290; y += 10) {
for(var x = 0; x <= 290; x += 10) {
if(z >= 1) {
z -= 1;
}
color = "rgb(0," + z + ",0)";
if(x % 20 === 0) {
context.fillStyle = color;
} else {
context.fillStyle = color;
}
context.fillRect(x, y, 10, 10);
}
}
}
}
}, false);
In summary, this code just fills the canvas with tiled rectangles of changing color. But how would one go about zooming in and out on something like this?
You'll want the scale method of context.
And note that, once you've drawn something on the canvas, it can't really be zoomed or scaled — you've got to re-draw the entire canvas at the new "zoom level".