This is my first submitted question. So please go easy!
I have made a very simple run/jump game using p5.js.
Game: https://simongowing1.github.io/the-art-collector-game/
Code: https://github.com/simongowing1/the-art-collector-game
Originally the 'player' was static, but I just added an animated gif (made up of two images) so that it appears to be walking.
When I check in the browser both layers of the gif show at once, so the effect does not work.
I think this is something to do with the rendering of the image. Having checked out some other solutions on stack, I tried 'createImage()' and 'createImg()' instead of simply 'loadimage()' but both of these resulted in other elements from the site disappearing.
Anyone have any ideas? Thanks in advance.
I've pasted my code from the 'game.js' file below for reference
class Game {
constructor() {
this.backgroundImage;
this.tokenImage;
}
setup() {
this.background = new Background();
this.player = new Player();
this.tokens = [];
this.obstacles = [];
}
preload() {
this.backgroundImage = [
{ src:loadImage('assets/empty_gallery_long.jpg'), x: 0, speed: 1.5}
]
this.playerImage = loadImage('assets/Player1-walking-front.gif');
this.playerImageBk = loadImage('assets/Player1-walking-back.gif');
this.tokenImage1 = loadImage('assets/token1.png');
this.tokenImage2 = loadImage('assets/token2.png');
this.tokenImage3 = loadImage('assets/token3.png');
this.obstacleImage = loadImage('assets/Obstacle1.png');
this.backgroundMusic = createAudio('assets/01 Windowlicker.mp3');
this.jumpSound = createAudio('assets/jump.mp3');
this.saleSound = createAudio('assets/Sale.m4a');
this.breakingSound = createAudio('assets/breaking.m4a')
}
gamelogic() {
if (counter === remainingTime) {}
}
draw() {
clear();
this.background.draw();
//console.log('hello')
if (frameCount === 10 || frameCount % 500 === 0) {
this.tokens.push(new Token(this.tokenImage1))
//console.log('now');
}
this.tokens.forEach(function (token) {
token.draw();
})
this.tokens = this.tokens.filter(
(token) => {
if (token.collision(this.player))
{
console.log('got it!');
return false
} else if (token.x < 0 - token.width) {
return false
} else {
return true
}
})
this.player.draw();
if (frameCount === 300 || frameCount % 1200 === 0) {
this.obstacles.push(new Obstacle(this.obstacleImage))
//console.log('now');
}
this.obstacles.forEach(function (obstacle) {
obstacle.draw();
})
this.obstacles = this.obstacles.filter(
(obstacle) => {
if (obstacle.collision(this.player))
{
// console.log('CRASH');
return false
} else {
return true
}
})
Split that gif into frames using free online editors like ezgif
Then name all frames in order player1, player2 and so on
and write code like this :-
this.playerImage = loadAnimation('player1', 'player2', 'player3');
Related
I am working on a whac a mole game where the background image should flash when the square is hit. For example an image that says "hit".
The square has been targeted correctly on function showImage(), tested with a console.log, and called in a forEach loop. I don't know the next step. I know I need to grab css class with background image of square and add an image. Maybe a set timer is involved. I have tried this and cannot get it working. See codepen
const squares = document.querySelectorAll('.square')
const mole = document.querySelector('.mole')
const timeLeft = document.querySelector('#time-left')
const score = document.querySelector('#score')
let result = 0
let hitPosition
let currentTime = 60
let timerId = null
function showImage() {
if ((document.onclick = squares)) {
squares.style.backgroundColor = 'green';
//console.log('it is working');
} else {
alert('it is not working');
}
}
function randomSquare() {
squares.forEach(square => {
square.classList.remove('mole')
})
let randomSquare = squares[Math.floor(Math.random() * 9)]
randomSquare.classList.add('mole')
hitPosition = randomSquare.id
}
squares.forEach(square => {
square.addEventListener('mousedown', () => {
if (square.id == hitPosition) {
result++
score.textContent = result
hitPosition = null
showImage();
}
})
})
function moveMole() {
timerId = setInterval(randomSquare, 500)
}
moveMole()
function countDown() {
currentTime--
timeLeft.textContent = currentTime
if (currentTime == 0) {
clearInterval(countDownTimerId)
clearInterval(timerId)
alert('GAME OVER! Your final score is ' + result)
}
}
let countDownTimerId = setInterval(countDown, 1000)
Sounds like this could handled by a class that displays an image in the background.
.bg-img {
background-image: url('');
}
And then in the function showImage() you set the class name bg-img on the element:
function showImage() {
squares.classList.add('bg-img');
setTimeout(function(){
squares.classList.remove('bg-img');
}, 1000);
}
And remove the class name again 1000 ms after.
I am testing twoway-motion.js on Aframe, by providing a simple way to navigate specifically without a device orientation permission from a mobile phone.
please check this glitch page for details: https://glitch.com/~scrawny-efraasia
also please see twoway-motion.js by #flowerio
AFRAME.registerComponent('twoway-motion', {
schema: {
speed: { type: "number", default: 40 },
threshold: { type: "number", default: -40 },
nonMobileLoad: { type: "boolean", default: false },
removeCheckpoints: {type: "boolean", default: true },
chatty: {type: "boolean", default: true }
},
init: function () {
var twowaymotion = document.querySelector("[camera]").components["twoway-motion"];
twowaymotion.componentName = "twoway-motion";
report = function(text) {
if (twowaymotion.data.chatty) {
console.log(twowaymotion.componentName, ":", text);
}
}
report("init.");
// report("asked to load with speed=", this.data.speed);
if (!AFRAME.utils.device.isMobile() && this.data.nonMobileLoad === false) {
// this is only for mobile devices.
//document.querySelector("[camera]").removeAttribute("twoway-motion");
report("Retired. Will only work on mobile.");
return;
} else {
if (this.data.nonMobileLoad === true) {
report("Loading on non-mobile platform.");
}
}
if (this.el.components["wasd-controls"] === undefined) {
this.el.setAttribute("wasd-controls", "true");
report("Installing wasd-controls.");
}
this.el.components["wasd-controls"].data.acceleration = this.data.speed;
// two-way hides checkpoint-controls by default.
if (this.data.removeCheckpoints) {
if (this.el.components["checkpoint-controls"] !== undefined) {
var checkpoints = document.querySelectorAll("[checkpoint]");
for (var cp = 0; cp < checkpoints.length; cp++) {
checkpoints[cp].setAttribute("visible", false);
}
}
}
this.el.removeAttribute("universal-controls");
if (this.el.components["look-controls"] === undefined) {
this.el.setAttribute("look-controls", "true");
}
var cur = document.querySelector("[cursor]");
if (cur !== null) {
console.log(this.componentName, ": found a cursor.");
this.cur = cur;
//this.curcolor = cur.getAttribute("material").color;
this.curcolor = cur.getAttribute("color");
} else {
console.log(this.componentName, ": didn't find a cursor.");
}
var canvas = document.querySelector(".a-canvas");
canvas.addEventListener("mousedown", function (e) {
report("mousedown", e);
twowaymotion.touching = true;
this.touchTime = new Date().getTime();
});
canvas.addEventListener("mouseup", function (e) {
report("mouseup", e);
twowaymotion.touching = false;
});
canvas.addEventListener("touchstart", function (e) {
this.touch = e;
report("touches.length: ", e.touches.length);
if (e.touches.length > 1) {
report("multitouch: doing nothing");
} else {
report("touchstart", e);
twowaymotion.touching = true;
}
});
canvas.addEventListener("touchend", function () {
console.log(this.componentName, " touchend");
twowaymotion.touching = false;
});
},
update: function() {
if (this.el.components["twoway-controls"] !== undefined) {
this.el.components["wasd-controls"].data.acceleration = this.el.components["wasd-controls"].data.speed;
}
},
tick: function () {
if (!AFRAME.utils.device.isMobile() && this.data.nonMobileLoad === false) {
// this is only for mobile devices, unless you ask for it.
return;
}
if (!this.isPlaying) {
return;
}
var cam = this.el;
var camrot = cam.getAttribute("rotation");
if (camrot.x < this.data.threshold) {
// we are looking down
if (this.cur !== null && this.cur !== undefined) {
this.cur.setAttribute("material", "color", "orange");
}
if (this.touching === true) {
cam.components["wasd-controls"].keys["ArrowDown"] = true;
} else {
cam.components["wasd-controls"].keys["ArrowDown"] = false;
cam.components["wasd-controls"].keys["ArrowUp"] = false;
}
} else {
// we are looking forward or up
if (this.cur !== null && this.cur !== undefined) {
this.cur.setAttribute("material", "color", this.curcolor);
}
if (this.touching === true) {
cam.components["wasd-controls"].keys["ArrowUp"] = true;
} else {
cam.components["wasd-controls"].keys["ArrowDown"] = false;
cam.components["wasd-controls"].keys["ArrowUp"] = false;
}
}
},
pause: function () {
// we get isPlaying automatically from A-Frame
},
play: function () {
// we get isPlaying automatically from A-Frame
},
remove: function () {
if (this.el.components["wasd-controls"] === undefined) {
this.el.removeAttribute("wasd-controls");
}
} });
Since device orientation permission is not granted on mobile phones, move backward is not working, also, when the audience tries to rotate in a different direction by touching the screen or sliding on screen, it still functions as move forward.
from what I imagine, if there is a simple edit, if the audience touches the screen more than 2 seconds, it start to move forward, if audience just rotate, it will not move forward, since when you slide or touch on the screen to rotate the touching time might not be so long as 2 seconds...
this is the easiest solution that I can imagine under the restriction of without device orientation permission.
or is there any other better way to divide rotate and move forward by touching screen regarding touching time?
Thks!!!!!
showMoves is a function made to show flashing for a simon game.
When the flashing lights are over I clear the interval to stop it and then I set game.playerTurn to true so I can click on colors, but game.playerTurn is changing to true as soon as showMoves is activated.
I want game.playerTurn to stay false until the showMoves function is finished showing flashing.
Here are the functions I'm using game.playerTurn in -
game.playerTurn = false;
//for flashing lights
function showMoves() {
let i = 0;
const start = setInterval(function () {
if (i >= game.computerMoves.length) {
clearInterval(start);
game.playerTurn = true;
return;
}
const move = game.computerMoves[i];
setLight(move, true);
setTimeout(setLight.bind(null, move, false), 1000); //Using bind to preset arguments
i++;
}, 2000);
}
function setLight(color, isOn) {
if (isOn) {
sounds[color.id].play();
}
color.style.backgroundColor = isOn ? colors[0].get(color) : colors[1].get(color);
}
//compareMoves is fired everytime I click on a color
function compareMoves(e) {
if (e === game.computerMoves[game.counter]) {
game.counter++;
//This is if all the moves were chosen correctly
if (game.playerMoves.length === game.computerMoves.length && e === game.computerMoves[game.computerMoves.length - 1]) {
simonHTML.displayScore.textContent = ++game.score;
game.playerTurn = false;
resetMoves();
randomMoves(++game.turn);
showMoves();
game.counter = 0;
}
} else if (game.strict) {
//if your move was wrong do this
} else {
game.playerMoves = [];
game.counter = 0;
game.playerTurn = false;
showMoves();
return false;
}
}
I'd appreciate any help with this. Here is a link to the game and all the code https://codepen.io/icewizard/pen/JLBpNQ
Where are you setting game.playerTurn back to false?
function showMoves() {
game.playerTurn = false;
let i = 0;
const start = setInterval(function() {
if (i >= game.computerMoves.length) {
clearInterval(start);
game.playerTurn = true;
return;
}
const move = game.computerMoves[i];
setLight(move, true);
setTimeout(setLight.bind(null, move, false), 1000); //Using bind to preset arguments
i++;
}, 2000);
}
Seems to work for me in the codepen example you provided
I built an interface that calls a web API in asp.net (i use c# and javascript/ajax to implement that).
The client side call to the controller, the controller needs to create animation gif and send it back to the client side by a string of base64 or byte array, when the client side gets the base64 he should display it into a canvas.
Now the problem is that the canvas display only the first frame of the animation gif like a static image.
I already read a lot on the internet and find this:
How Do I Convert A GIF Animation To Base64 String And Back To A GIF Animation?
But it's not helped me because I don't want to save the image on the disc just to display it on the client side.
*Note that when I save the image from server side on my disc its save it as gif and display all frames together like I wish, something wrong when I transfer it to client side.
*I use ImageMagick to create the animated gif.
Here is my client side code:
<!DOCTYPE html>
<html>
<head>
<title></title>
<meta charset="utf-8" />
<link href="Content/bootstrap.min.css" rel="stylesheet" />
</head>
<body style="padding-top: 20px;">
<div class="col-md-10 col-md-offset-1">
<div class="well">
<!---->
<canvas id="canvasImage" width="564" height="120">
<p>We apologize, your browser does not support canvas at this time!</p>
</canvas>
<!---->
</div>
</div>
<script src="Scripts/jquery-1.10.2.min.js"></script>
<script src="Scripts/bootstrap.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
$.ajax({
url: '/api/EngineProccess',
method: 'GET',
success: function (data) {
var imageObj = new Image();
var canvas = document.getElementById("canvasImage");
var context = canvas.getContext('2d');
var image = new Image();
image.onload = function () {
context.drawImage(image, 0, 0);
};
console.log(data);
image.src = "data:image/gif;base64," + data;
},
error: function (jqXHR) {
$('#divErrorText').text(jqXHR.responseText);
$('#divError').show('fade');
}
});
});
</script>
</body>
</html>
and here is the server code:
public class EngineProccessController : ApiController
{
// GET api/EngineProccess
public String Get()
{
using (MagickImageCollection collection = new MagickImageCollection())
{
// Add first image and set the animation delay to 100ms
collection.Add("Snakeware1.gif");
collection[0].AnimationDelay = 100;
// Add second image, set the animation delay to 100ms and flip the image
collection.Add("Snakeware2.gif");
collection[1].AnimationDelay = 100;
collection[1].Flip();
// Optionally reduce colors
QuantizeSettings settings = new QuantizeSettings();
settings.Colors = 256;
collection.Quantize(settings);
// Optionally optimize the images (images should have the same size).
collection.Optimize();
// Save gif
//collection.Write("D://Test01//Test01//Animated.gif");
string data = collection.ToBase64();
return data;
}
}
}
Any ideas?
Please help.
Edit: after some days i found the problem, i use magicimage (magic.net) to create the gif animaited, the base64 was ok but the problem was in the canvas element, the canvas didnt display the animation likei want so i changed the element canvas to be an regular image element () and the changed the src of the image dynamic.
Regards,
Jr.Rafa
Example loading playing gif on canvas.
Sorry but just under 30K answer limit, code and comment very cut down to fit. Ask in comments if needed. See bottom of snippet on basic usage.
/*
The code was created as to the specifications set out in https://www.w3.org/Graphics/GIF/spec-gif89a.txt
The document states usage conditions
"The Graphics Interchange Format(c) is the Copyright property of
CompuServe Incorporated. GIF(sm) is a Service Mark property of
CompuServe Incorporated."
https://en.wikipedia.org/wiki/GIF#Unisys_and_LZW_patent_enforcement last paragraph
Additional sources
https://en.wikipedia.org/wiki/GIF
https://www.w3.org/Graphics/GIF/spec-gif87.txt
*/
var GIF = function () {
var timerID;
var st;
var interlaceOffsets = [0, 4, 2, 1]; // used in de-interlacing.
var interlaceSteps = [8, 8, 4, 2];
var interlacedBufSize = undefined;
var deinterlaceBuf = undefined;
var pixelBufSize = undefined;
var pixelBuf = undefined;
const GIF_FILE = {
GCExt : 0xF9,
COMMENT : 0xFE,
APPExt : 0xFF,
UNKNOWN : 0x01,
IMAGE : 0x2C,
EOF : 59,
EXT : 0x21,
};
var Stream = function (data) { // simple buffered stream
this.data = new Uint8ClampedArray(data);
this.pos = 0;
var len = this.data.length;
this.getString = function (count) {
var s = "";
while (count--) {
s += String.fromCharCode(this.data[this.pos++]);
}
return s;
};
this.readSubBlocks = function () {
var size, count, data;
data = "";
do {
count = size = this.data[this.pos++];
while (count--) {
data += String.fromCharCode(this.data[this.pos++]);
}
} while (size !== 0 && this.pos < len);
return data;
}
this.readSubBlocksB = function () { // reads a set of blocks as binary
var size, count, data;
data = [];
do {
count = size = this.data[this.pos++];
while (count--) {
data.push(this.data[this.pos++]);
}
} while (size !== 0 && this.pos < len);
return data;
}
};
// LZW decoder uncompressed each frame's pixels
var lzwDecode = function (minSize, data) {
var i, pixelPos, pos, clear, eod, size, done, dic, code, last, d, len;
pos = 0;
pixelPos = 0;
dic = [];
clear = 1 << minSize;
eod = clear + 1;
size = minSize + 1;
done = false;
while (!done) {
last = code;
code = 0;
for (i = 0; i < size; i++) {
if (data[pos >> 3] & (1 << (pos & 7))) {
code |= 1 << i;
}
pos++;
}
if (code === clear) { // clear and reset the dictionary
dic = [];
size = minSize + 1;
for (i = 0; i < clear; i++) {
dic[i] = [i];
}
dic[clear] = [];
dic[eod] = null;
continue;
}
if (code === eod) { // end of data
done = true;
return;
}
if (code >= dic.length) {
dic.push(dic[last].concat(dic[last][0]));
} else
if (last !== clear) {
dic.push(dic[last].concat(dic[code][0]));
}
d = dic[code];
len = d.length;
for (i = 0; i < len; i++) {
pixelBuf[pixelPos++] = d[i];
}
if (dic.length === (1 << size) && size < 12) {
size++;
}
}
};
var parseColourTable = function (count) { // get a colour table of length count
// Each entry is 3 bytes, for RGB.
var colours = [];
for (var i = 0; i < count; i++) {
colours.push([st.data[st.pos++], st.data[st.pos++], st.data[st.pos++]]);
}
return colours;
};
var parse = function () { // read the header. This is the starting point of the decode and async calls parseBlock
var bitField;
st.pos += 6; // skip the first stuff see GifEncoder for details
gif.width = (st.data[st.pos++]) + ((st.data[st.pos++]) << 8);
gif.height = (st.data[st.pos++]) + ((st.data[st.pos++]) << 8);
bitField = st.data[st.pos++];
gif.colorRes = (bitField & 0b1110000) >> 4;
gif.globalColourCount = 1 << ((bitField & 0b111) + 1);
gif.bgColourIndex = st.data[st.pos++];
st.pos++; // ignoring pixel aspect ratio. if not 0, aspectRatio = (pixelAspectRatio + 15) / 64
if (bitField & 0b10000000) { // global colour flag
gif.globalColourTable = parseColourTable(gif.globalColourCount);
}
setTimeout(parseBlock, 0);
};
var parseAppExt = function () { // get application specific data. Netscape added iterations and terminator. Ignoring that
st.pos += 1;
if ('NETSCAPE' === st.getString(8)) {
st.pos += 8; // ignoring this data. iterations (word) and terminator (byte)
} else {
st.pos += 3; // 3 bytes of string usually "2.0" when identifier is NETSCAPE
st.readSubBlocks(); // unknown app extension
}
};
var parseGCExt = function () { // get GC data
var bitField;
st.pos++;
bitField = st.data[st.pos++];
gif.disposalMethod = (bitField & 0b11100) >> 2;
gif.transparencyGiven = bitField & 0b1 ? true : false; // ignoring bit two that is marked as userInput???
gif.delayTime = (st.data[st.pos++]) + ((st.data[st.pos++]) << 8);
gif.transparencyIndex = st.data[st.pos++];
st.pos++;
};
var parseImg = function () { // decodes image data to create the indexed pixel image
var deinterlace, frame, bitField;
deinterlace = function (width) { // de interlace pixel data if needed
var lines, fromLine, pass, toline;
lines = pixelBufSize / width;
fromLine = 0;
if (interlacedBufSize !== pixelBufSize) {
deinterlaceBuf = new Uint8Array(pixelBufSize);
interlacedBufSize = pixelBufSize;
}
for (pass = 0; pass < 4; pass++) {
for (toLine = interlaceOffsets[pass]; toLine < lines; toLine += interlaceSteps[pass]) {
deinterlaceBuf.set(pixelBuf.subArray(fromLine, fromLine + width), toLine * width);
fromLine += width;
}
}
};
frame = {}
gif.frames.push(frame);
frame.disposalMethod = gif.disposalMethod;
frame.time = gif.length;
frame.delay = gif.delayTime * 10;
gif.length += frame.delay;
if (gif.transparencyGiven) {
frame.transparencyIndex = gif.transparencyIndex;
} else {
frame.transparencyIndex = undefined;
}
frame.leftPos = (st.data[st.pos++]) + ((st.data[st.pos++]) << 8);
frame.topPos = (st.data[st.pos++]) + ((st.data[st.pos++]) << 8);
frame.width = (st.data[st.pos++]) + ((st.data[st.pos++]) << 8);
frame.height = (st.data[st.pos++]) + ((st.data[st.pos++]) << 8);
bitField = st.data[st.pos++];
frame.localColourTableFlag = bitField & 0b10000000 ? true : false;
if (frame.localColourTableFlag) {
frame.localColourTable = parseColourTable(1 << ((bitField & 0b111) + 1));
}
if (pixelBufSize !== frame.width * frame.height) { // create a pixel buffer if not yet created or if current frame size is different from previous
pixelBuf = new Uint8Array(frame.width * frame.height);
pixelBufSize = frame.width * frame.height;
}
lzwDecode(st.data[st.pos++], st.readSubBlocksB()); // decode the pixels
if (bitField & 0b1000000) { // de interlace if needed
frame.interlaced = true;
deinterlace(frame.width);
} else {
frame.interlaced = false;
}
processFrame(frame); // convert to canvas image
};
var processFrame = function (frame) {
var ct, cData, dat, pixCount, ind, useT, i, pixel, pDat, col, frame, ti;
frame.image = document.createElement('canvas');
frame.image.width = gif.width;
frame.image.height = gif.height;
frame.image.ctx = frame.image.getContext("2d");
ct = frame.localColourTableFlag ? frame.localColourTable : gif.globalColourTable;
if (gif.lastFrame === null) {
gif.lastFrame = frame;
}
useT = (gif.lastFrame.disposalMethod === 2 || gif.lastFrame.disposalMethod === 3) ? true : false;
if (!useT) {
frame.image.ctx.drawImage(gif.lastFrame.image, 0, 0, gif.width, gif.height);
}
cData = frame.image.ctx.getImageData(frame.leftPos, frame.topPos, frame.width, frame.height);
ti = frame.transparencyIndex;
dat = cData.data;
if (frame.interlaced) {
pDat = deinterlaceBuf;
} else {
pDat = pixelBuf;
}
pixCount = pDat.length;
ind = 0;
for (i = 0; i < pixCount; i++) {
pixel = pDat[i];
col = ct[pixel];
if (ti !== pixel) {
dat[ind++] = col[0];
dat[ind++] = col[1];
dat[ind++] = col[2];
dat[ind++] = 255; // Opaque.
} else
if (useT) {
dat[ind + 3] = 0; // Transparent.
ind += 4;
} else {
ind += 4;
}
}
frame.image.ctx.putImageData(cData, frame.leftPos, frame.topPos);
gif.lastFrame = frame;
if (!gif.waitTillDone && typeof gif.onload === "function") { // if !waitTillDone the call onload now after first frame is loaded
doOnloadEvent();
}
};
var finnished = function () { // called when the load has completed
gif.loading = false;
gif.frameCount = gif.frames.length;
gif.lastFrame = null;
st = undefined;
gif.complete = true;
gif.disposalMethod = undefined;
gif.transparencyGiven = undefined;
gif.delayTime = undefined;
gif.transparencyIndex = undefined;
gif.waitTillDone = undefined;
pixelBuf = undefined; // dereference pixel buffer
deinterlaceBuf = undefined; // dereference interlace buff (may or may not be used);
pixelBufSize = undefined;
deinterlaceBuf = undefined;
gif.currentFrame = 0;
if (gif.frames.length > 0) {
gif.image = gif.frames[0].image;
}
doOnloadEvent();
if (typeof gif.onloadall === "function") {
(gif.onloadall.bind(gif))({
type : 'loadall',
path : [gif]
});
}
if (gif.playOnLoad) {
gif.play();
}
}
var canceled = function () { // called if the load has been cancelled
finnished();
if (typeof gif.cancelCallback === "function") {
(gif.cancelCallback.bind(gif))({
type : 'canceled',
path : [gif]
});
}
}
var parseExt = function () { // parse extended blocks
switch (st.data[st.pos++]) {
case GIF_FILE.GCExt:
parseGCExt();
break;
case GIF_FILE.COMMENT:
gif.comment += st.readSubBlocks(); // found a comment field
break;
case GIF_FILE.APPExt:
parseAppExt();
break;
case GIF_FILE.UNKNOWN: // not keeping this data
st.pos += 13; // deliberate fall through to default
default: // not keeping this if it happens
st.readSubBlocks();
break;
}
}
var parseBlock = function () { // parsing the blocks
if (gif.cancel !== undefined && gif.cancel === true) {
canceled();
return;
}
switch (st.data[st.pos++]) {
case GIF_FILE.IMAGE: // image block
parseImg();
if (gif.firstFrameOnly) {
finnished();
return;
}
break;
case GIF_FILE.EOF: // EOF found so cleanup and exit.
finnished();
return;
case GIF_FILE.EXT: // extend block
default:
parseExt();
break;
}
if (typeof gif.onprogress === "function") {
gif.onprogress({
bytesRead : st.pos,
totalBytes : st.data.length,
frame : gif.frames.length
});
}
setTimeout(parseBlock, 0);
};
var cancelLoad = function (callback) {
if (gif.complete) {
return false;
}
gif.cancelCallback = callback;
gif.cancel = true;
return true;
}
var error = function (type) {
if (typeof gif.onerror === "function") {
(gif.onerror.bind(this))({
type : type,
path : [this]
});
}
gif.onerror = undefined;
gif.onload = undefined;
gif.loading = false;
}
var doOnloadEvent = function () { // fire onload event if set
gif.currentFrame = 0;
gif.lastFrameAt = new Date().valueOf();
gif.nextFrameAt = new Date().valueOf();
if (typeof gif.onload === "function") {
(gif.onload.bind(gif))({
type : 'load',
path : [gif]
});
}
gif.onload = undefined;
gif.onerror = undefined;
}
var dataLoaded = function (data) {
st = new Stream(data);
parse();
}
var loadGif = function (filename) { // starts the load
var ajax = new XMLHttpRequest();
ajax.responseType = "arraybuffer";
ajax.onload = function (e) {
if (e.target.status === 400) {
error("Bad Request response code");
} else if (e.target.status === 404) {
error("File not found");
} else {
dataLoaded(ajax.response);
}
};
ajax.open('GET', filename, true);
ajax.send();
ajax.onerror = function (e) {
error("File error");
};
this.src = filename;
this.loading = true;
}
function play() { // starts play if paused
if (!gif.playing) {
gif.paused = false;
gif.playing = true;
playing();
}
}
function pause() { // stops play
gif.paused = true;
gif.playing = false;
clearTimeout(timerID);
}
function togglePlay(){
if(gif.paused || !gif.playing){
gif.play();
}else{
gif.pause();
}
}
function seekFrame(frame) { // seeks to frame number.
clearTimeout(timerID);
frame = frame < 0 ? (frame % gif.frames.length) + gif.frames.length : frame;
gif.currentFrame = frame % gif.frames.length;
if (gif.playing) {
playing();
} else {
gif.image = gif.frames[gif.currentFrame].image;
}
}
function seek(time) { // time in Seconds
clearTimeout(timerID);
if (time < 0) {
time = 0;
}
time *= 1000; // in ms
time %= gif.length;
var frame = 0;
while (time > gif.frames[frame].time + gif.frames[frame].delay && frame < gif.frames.length) {
frame += 1;
}
gif.currentFrame = frame;
if (gif.playing) {
playing();
} else {
gif.image = gif.frames[gif.currentFrame].image;
}
}
function playing() {
var delay;
var frame;
if (gif.playSpeed === 0) {
gif.pause();
return;
}
if (gif.playSpeed < 0) {
gif.currentFrame -= 1;
if (gif.currentFrame < 0) {
gif.currentFrame = gif.frames.length - 1;
}
frame = gif.currentFrame;
frame -= 1;
if (frame < 0) {
frame = gif.frames.length - 1;
}
delay = -gif.frames[frame].delay * 1 / gif.playSpeed;
} else {
gif.currentFrame += 1;
gif.currentFrame %= gif.frames.length;
delay = gif.frames[gif.currentFrame].delay * 1 / gif.playSpeed;
}
gif.image = gif.frames[gif.currentFrame].image;
timerID = setTimeout(playing, delay);
}
var gif = { // the gif image object
onload : null, // fire on load. Use waitTillDone = true to have load fire at end or false to fire on first frame
onerror : null, // fires on error
onprogress : null, // fires a load progress event
onloadall : null, // event fires when all frames have loaded and gif is ready
paused : false, // true if paused
playing : false, // true if playing
waitTillDone : true, // If true onload will fire when all frames loaded, if false, onload will fire when first frame has loaded
loading : false, // true if still loading
firstFrameOnly : false, // if true only load the first frame
width : null, // width in pixels
height : null, // height in pixels
frames : [], // array of frames
comment : "", // comments if found in file. Note I remember that some gifs have comments per frame if so this will be all comment concatenated
length : 0, // gif length in ms (1/1000 second)
currentFrame : 0, // current frame.
frameCount : 0, // number of frames
playSpeed : 1, // play speed 1 normal, 2 twice 0.5 half, -1 reverse etc...
lastFrame : null, // temp hold last frame loaded so you can display the gif as it loads
image : null, // the current image at the currentFrame
playOnLoad : true, // if true starts playback when loaded
// functions
load : loadGif, // call this to load a file
cancel : cancelLoad, // call to stop loading
play : play, // call to start play
pause : pause, // call to pause
seek : seek, // call to seek to time
seekFrame : seekFrame, // call to seek to frame
togglePlay : togglePlay, // call to toggle play and pause state
};
return gif;
}
/*=================================================================
USEAGE Example below
Image used from Wiki see HTML for requiered image atribution
===================================================================*/
const ctx = canvas.getContext("2d");
ctx.font = "16px arial";
var changeFrame = false;
var changeSpeed = false;
frameNum.addEventListener("mousedown",()=>{changeFrame = true ; changeSpeed = false});
speedInput.addEventListener("mousedown",()=>{changeSpeed = true; changeFrame = false});
const gifSrc = "https://upload.wikimedia.org/wikipedia/commons/thumb/9/97/Odessa_TX_Oil_Well_with_Lufkin_320D_pumping_unit.gif/220px-Odessa_TX_Oil_Well_with_Lufkin_320D_pumping_unit.gif"
var myGif = GIF(); // Creates a new gif
myGif.load(gifSrc); // set URL and load
myGif.onload = function(event){ // fires when loading is complete
frameNum.max = myGif.frameCount-1;
animate();
}
myGif.onprogress = function(event){ // Note this function is not bound to myGif
if(canvas.width !== myGif.width || canvas.height !== myGif.height){
canvas.width = myGif.width;
canvas.height = myGif.height;
ctx.font = "16px arial";
}
if(myGif.lastFrame !== null){
ctx.drawImage(myGif.lastFrame.image,0,0);
}
ctx.fillStyle = "black";
ctx.fillText("Loaded frame "+event.frame,8,20);
frameNum.max = event.frame-1;
frameNum.value = event.frame;
frameText.textContent = frameNum.value + "/" + (frameNum.max-1);
}
myGif.onerror = function(event){
ctx.fillStyle = "black";
ctx.fillText("Could not load the Gif ",8,20);
ctx.fillText("Error : " + event.type,8,40);
}
function animate(){
if(changeFrame){
if(myGif.playing){
myGif.pause();
}
myGif.seekFrame(Number(frameNum.value));
}else if(changeSpeed){
myGif.playSpeed = speedInput.value;
if(myGif.paused){
myGif.play();
}
}
frameNum.value = myGif.currentFrame;
frameText.textContent = frameNum.value + "/" + frameNum.max;
if(myGif.paused){
speedInput.value = 0;
}else{
speedInput.value = myGif.playSpeed;
}
speedText.textContent = speedInput.value;
ctx.drawImage(myGif.image,0,0);
requestAnimationFrame(animate);
}
canvas { border : 2px solid black; }
p { font-family : arial; font-size : 12px }
<canvas id="canvas"></canvas>
<div id="inputs">
<input id="frameNum" type = "range" min="0" max="1" step="1" value="0"></input>
Frame : <span id="frameText"></span><br>
<input id="speedInput" type = "range" min="-3" max="3" step="0.1" value="1"></input>
Speed : <span id="speedText"></span><br>
</div>
<p>Image source <br>By DASL51984 - Original YouTube video by user "derekdz", looped by DASL51984, CC BY-SA 4.0, Link</p>
I'm trying to create a Maze game using the Tiled map editor and Phaser. I am using this tutorial as a base: http://phaser.io/tutorials/coding-tips-005
But my map is not showing in my browser. I have created a tilemap and exported it as a json file. And there is an error in the code saying Uncaught ReferenceError: Phaser is not defined". What am I missing or doing incorrectly?
This is the code:
<!DOCTYPE HTML>
<html>
<head>
<title>Maze Game</title>
<meta charset="utf-8">
<script src="//cdn.jsdelivr.net/phaser/2.2.2/phaser.min.js"></script>
</head>
<body>
<div id="game"></div>
<script type="text/javascript">
var game = new Phaser.Game(640, 480, Phaser.AUTO, 'game');
var PhaserGame = function (game) {
this.map = null;
this.layer = null;
this.car = null;
this.safetile = 1;
this.gridsize = 32;
this.speed = 150;
this.threshold = 3;
this.turnSpeed = 150;
this.marker = new Phaser.Point();
this.turnPoint = new Phaser.Point();
this.directions = [ null, null, null, null, null ];
this.opposites = [ Phaser.NONE, Phaser.RIGHT, Phaser.LEFT, Phaser.DOWN, Phaser.UP ];
this.current = Phaser.UP;
this.turning = Phaser.NONE;
};
PhaserGame.prototype = {
init: function () {
this.physics.startSystem(Phaser.Physics.ARCADE);
},
preload: function () {
// We need this because the assets are on Amazon S3
// Remove the next 2 lines if running locally
this.load.baseURL = 'http://files.phaser.io.s3.amazonaws.com/codingtips/issue005/';
this.load.crossOrigin = 'anonymous';
this.load.tilemap('map', 'assets/samplemaze.json', null, Phaser.Tilemap.TILED_JSON);
this.load.image('tiles', 'assets/tiles.png');
this.load.image('car', 'assets/car.png');
// Note: Graphics are Copyright 2015 Photon Storm Ltd.
},
create: function () {
this.map = this.add.tilemap('map');
this.map.addTilesetImage('tiles', 'tiles');
this.layer = this.map.createLayer('Tile Layer 1');
this.map.setCollision(20, true, this.layer);
this.car = this.add.sprite(48, 48, 'car');
this.car.anchor.set(0.5);
this.physics.arcade.enable(this.car);
this.cursors = this.input.keyboard.createCursorKeys();
this.move(Phaser.DOWN);
},
checkKeys: function () {
if (this.cursors.left.isDown && this.current !== Phaser.LEFT)
{
this.checkDirection(Phaser.LEFT);
}
else if (this.cursors.right.isDown && this.current !== Phaser.RIGHT)
{
this.checkDirection(Phaser.RIGHT);
}
else if (this.cursors.up.isDown && this.current !== Phaser.UP)
{
this.checkDirection(Phaser.UP);
}
else if (this.cursors.down.isDown && this.current !== Phaser.DOWN)
{
this.checkDirection(Phaser.DOWN);
}
else
{
// This forces them to hold the key down to turn the corner
this.turning = Phaser.NONE;
}
},
checkDirection: function (turnTo) {
if (this.turning === turnTo || this.directions[turnTo] === null || this.directions[turnTo].index !== this.safetile)
{
// Invalid direction if they're already set to turn that way
// Or there is no tile there, or the tile isn't index a floor tile
return;
}
// Check if they want to turn around and can
if (this.current === this.opposites[turnTo])
{
this.move(turnTo);
}
else
{
this.turning = turnTo;
this.turnPoint.x = (this.marker.x * this.gridsize) + (this.gridsize / 2);
this.turnPoint.y = (this.marker.y * this.gridsize) + (this.gridsize / 2);
}
},
turn: function () {
var cx = Math.floor(this.car.x);
var cy = Math.floor(this.car.y);
// This needs a threshold, because at high speeds you can't turn because the coordinates skip past
if (!this.math.fuzzyEqual(cx, this.turnPoint.x, this.threshold) || !this.math.fuzzyEqual(cy, this.turnPoint.y, this.threshold))
{
return false;
}
this.car.x = this.turnPoint.x;
this.car.y = this.turnPoint.y;
this.car.body.reset(this.turnPoint.x, this.turnPoint.y);
this.move(this.turning);
this.turning = Phaser.NONE;
return true;
},
move: function (direction) {
var speed = this.speed;
if (direction === Phaser.LEFT || direction === Phaser.UP)
{
speed = -speed;
}
if (direction === Phaser.LEFT || direction === Phaser.RIGHT)
{
this.car.body.velocity.x = speed;
}
else
{
this.car.body.velocity.y = speed;
}
this.add.tween(this.car).to( { angle: this.getAngle(direction) }, this.turnSpeed, "Linear", true);
this.current = direction;
},
getAngle: function (to) {
// About-face?
if (this.current === this.opposites[to])
{
return "180";
}
if ((this.current === Phaser.UP && to === Phaser.LEFT) ||
(this.current === Phaser.DOWN && to === Phaser.RIGHT) ||
(this.current === Phaser.LEFT && to === Phaser.DOWN) ||
(this.current === Phaser.RIGHT && to === Phaser.UP))
{
return "-90";
}
return "90";
},
update: function () {
this.physics.arcade.collide(this.car, this.layer);
this.marker.x = this.math.snapToFloor(Math.floor(this.car.x), this.gridsize) / this.gridsize;
this.marker.y = this.math.snapToFloor(Math.floor(this.car.y), this.gridsize) / this.gridsize;
// Update our grid sensors
this.directions[1] = this.map.getTileLeft(this.layer.index, this.marker.x, this.marker.y);
this.directions[2] = this.map.getTileRight(this.layer.index, this.marker.x, this.marker.y);
this.directions[3] = this.map.getTileAbove(this.layer.index, this.marker.x, this.marker.y);
this.directions[4] = this.map.getTileBelow(this.layer.index, this.marker.x, this.marker.y);
this.checkKeys();
if (this.turning !== Phaser.NONE)
{
this.turn();
}
},
render: function () {
// Un-comment this to see the debug drawing
for (var t = 1; t < 5; t++)
{
if (this.directions[t] === null)
{
continue;
}
var color = 'rgba(0,255,0,0.3)';
if (this.directions[t].index !== this.safetile)
{
color = 'rgba(255,0,0,0.3)';
}
if (t === this.current)
{
color = 'rgba(255,255,255,0.3)';
}
this.game.debug.geom(new Phaser.Rectangle(this.directions[t].worldX, this.directions[t].worldY, 32, 32), color, true);
}
this.game.debug.geom(this.turnPoint, '#ffff00');
}
};
game.state.add('Game', PhaserGame, true);
</script>
<img src="http://files.phaser.io.s3.amazonaws.com/codingtips/issue005/phaser-tips-header1.png" title="Phaser Coding Tips Weekly" style="margin-top: 8px" />
</body>
</html>
Thank you so much! I'm pretty new to programming so any feedback would be very useful!
You might be missing "http:" or "https:" in your script tag's src attribute. If so, then the phaser.min.js file isn't being included in your page and could result in undefined references.