What I would like to to do is the following:
When I press the left arrow only marioleft is drawn.
When the right arrow is pressed it only draws marioright.
The problem is that both images are loaded at the same time, and they also move at the same time. I would like the 2 to move with the right key. What is wrong with my code?
I thought that I should put a condition on the load function, but I do not know how to do that.
What type of condition should I use?
if (), if else, switch ... and how do I use them?
var vp = document.getElementById("villa_mario");
var paper = vp.getContext("2d");
document.addEventListener("keydown", moveMario);
var x = 250;
var y = 250;
var keys = {
UP: 38,
DOWN: 40,
LEFT: 37,
RIGHT: 39
};
var background = {
url: "tile.png",
loadOK: false
}
var marioright = {
url: "mario_right.png",
loadOK: false
}
var marioleft = {
url: "mario_left.png",
loadOK: false
}
background.imagen = new Image();
background.imagen.src = background.url;
background.imagen.addEventListener("load", loadBackground);
marioright.imagen = new Image();
marioright.imagen.src = marioright.url;
marioright.imagen.addEventListener("load", loadMarioright);
marioleft.imagen = new Image();
marioleft.imagen.src = marioleft.url;
marioleft.imagen.addEventListener("load", loadMarioleft);
function loadBackground()
{
background.loadOK = true
paint();
}
function loadMarioright()
{
marioright.loadOK = true
paint();
}
function loadMarioleft()
{
marioleft.loadOK = true
paint();
}
function paint()
{
if(background.loadOK)
{
paper.drawImage(background.imagen, 0, 0);
}
if(marioright.loadOK)
{
paper.drawImage(marioright.imagen, x, y);
}
if(marioleft.loadOK)
{
paper.drawImage(marioleft.imagen, x, y);
}
}
function moveMario(evento)
{
var movement = 5;
switch(evento.keyCode)
{
case keys.RIGHT:
loadMarioright(x, y, x + movement, y, paper);
x = x + movement;
break;
case keys.LEFT:
loadMarioleft(x, y, x - movement, y, paper);
x = x - movement;
break;
default:
console.log("other key");
break;
}
}
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Villa Mario</title>
</head>
<body>
<canvas id="villa_mario" width="500" height="500">
</canvas>
<script src="mario.js"></script>
</body>
</html>
You have to set false to 'loadOk' variable.
Update the loadMarioright(),loadMarioleft() functions as follows.
function loadMarioright()
{
marioright.loadOK = true;
marioleft.loadOK = false;
paint();
}
function loadMarioleft()
{
marioleft.loadOK = true;
marioright.loadOK = false;
paint();
}
Ragu bathi is right in that you need to toggle loadOK off in order to make it load correctly, but I would suggest removing the extra complication of having toggles on every image by forcing it to only draw one Mario every time you paint. The way you control it this time is by just selecting which image you want to use.
var marioToDraw = marioright;
function paint() {
if(background.loadOK) {
paper.drawImage(background.imagen, 0, 0);
}
//draw the selected Mario
paper.drawImage(marioToDraw.imagen, x, y);
}
//inside the switch statement
switch(evento.keyCode) {
case keys.RIGHT:
marioToDraw = marioright;
x = x + movement;
break;
case keys.LEFT:
marioToDraw = marioleft;
x = x - movement;
break;
default:
console.log("A different key was pressed!");
}
paint();
Related
I'm trying to have the sprite "Pounce" move with the arrow keys and the sprite "gsu.G" move witht he arrow keys. Both should be able to appear on the canvas and move at the same time, but for some reason only gsu.G shows up. Could I get some help with this code, I have tried everything. I'm coding in notepad++ javascript.
<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<title>Simple Animation</title>
<script type="application/javascript">
// <![CDATA[
var INTERVAL = 0;
var DEBUG = false; //true;
var canvas; // The canvas shown on the page.
var ctx; // The context, used to access the canvas.
var SpriteRow = 0; // Row of the graphic to show
var SpriteCol = 0; // Col of the graphic to show
var MaxSpriteRow = 8; // How many rows of images
var MaxSpriteCol = 8; // How many columns of images
var SpriteX = 100; // Position of sprite on the canvas
var SpriteY = 100;
var SpriteWidth = 32; // Width, Height of each subimage
var SpriteHeight = 32;
var SpriteImage = new Image(); // Sprite sheet
SpriteImage.src = "Pounce.png";
// Set up a timer to execute every 50 ms.
var myInterval;
function eraseSprite() {
// erase sprite
ctx.clearRect(SpriteX, SpriteY, SpriteWidth, SpriteHeight);
}
function drawSprite() {
// draw sprite
//ctx.drawImage(SpriteImage, SpriteX, SpriteY);
ctx.drawImage(SpriteImage, SpriteCol * SpriteWidth, SpriteRow * SpriteHeight,
SpriteWidth, SpriteHeight, SpriteX, SpriteY, SpriteWidth, SpriteHeight);
// update the next image to show
// SpriteCol++;
if (SpriteCol >= MaxSpriteCol)
SpriteCol = 0;
}
function Tick() {
// Erase the sprite from its current location.
eraseSprite();
// Show a new image
drawSprite();
}
function loadComplete() {
console.log("Load is complete.");
canvas = document.getElementById("theCanvas");
ctx = canvas.getContext("2d");
myInterval = self.setInterval(function(){Tick()}, INTERVAL);
}
// What to do when the user presses a key.
function whenKeyPressed(key) {
switch (key) {
case 28: // Right arrow was pressed
if(SpriteX < 612){
eraseSprite();
SpriteX++;
drawSprite();
}
break;
case 29: // Left arrow, ASCII 29
if(SpriteX > 0){
eraseSprite();
SpriteX--;
drawSprite();
}
break;
case 30: // Up arrow was pressed
if(SpriteY > 0){
eraseSprite();
SpriteY--;
drawSprite();
}
break;
case 31: // Down arrow was pressed
if(SpriteY < 457) {
eraseSprite();
SpriteY++;
drawSprite();
}
break;
}
}
///]]>
</script>
</head>
<body onload="loadComplete()" bgcolor="#0f0000" text="#ffffff">
<center>
<canvas id="theCanvas" tabindex="1" width="640" height="480"></canvas>
<h2>Press up/down to see different rows</h2>
</center>
<script type="application/javascript">
//
// Set up a function to handle key-presses.
//
// This should work across most browsers.
document['onkeydown'] = function(event) {
event = event || window.event;
var key = event.which || event.cursor;
// Check for a special key value, and map it to ASCII.
switch (key) {
case 37: // Left arrow, ASCII 29
key = 29;
break;
case 38: // Up arrow, ASCII 30
key = 30;
break;
case 39: // Right arrow, ASCII 28
key = 28;
break;
case 40: // Down arrow, ASCII 31
key = 31;
break;
}
//document.getElementById("keydown").innerHTML =
// " key Down event, keycode " + key;
whenKeyPressed(key);
};
</script>
<script type="application/javascript">
// <![CDATA[
var INTERVAL = 0;
var DEBUG = false; //true;
var SpriteRow = 0; // Row of the graphic to show
var SpriteCol = 0; // Col of the graphic to show
var MaxSpriteRow = 8; // How many rows of images
var MaxSpriteCol = 8; // How many columns of images
var SpriteX = 100; // Position of sprite on the canvas
var SpriteY = 100;
var SpriteWidth = 32; // Width, Height of each subimage
var SpriteHeight = 32;
var SpriteImage = new Image(); // Sprite sheet
SpriteImage.src = "Gsug.png";
// Set up a timer to execute every 50 ms.
var myInterval;
function eraseSprite() {
// erase sprite
ctx.clearRect(SpriteX, SpriteY, SpriteWidth, SpriteHeight);
}
function drawSprite() {
// draw sprite
//ctx.drawImage(SpriteImage, SpriteX, SpriteY);
ctx.drawImage(SpriteImage, SpriteCol * SpriteWidth, SpriteRow * SpriteHeight,
SpriteWidth, SpriteHeight, SpriteX, SpriteY, SpriteWidth, SpriteHeight);
// update the next image to show
// SpriteCol++;
if (SpriteCol >= MaxSpriteCol)
SpriteCol = 0;
}
function Tick() {
// Erase the sprite from its current location.
eraseSprite();
// Show a new image
drawSprite();
}
function loadComplete() {
console.log("Load is complete.");
canvas = document.getElementById("theCanvas");
ctx = canvas.getContext("2d");
myInterval = self.setInterval(function(){Tick()}, INTERVAL);
}
// What to do when the user presses a key.
function whenKeyPressed(key) {
switch (key) {
case 68: // Right arrow was pressed
if(SpriteX < 612){
eraseSprite();
SpriteX++;
drawSprite();
}
break;
case 65: // Left arrow, ASCII 29
if(SpriteX > 0){
eraseSprite();
SpriteX--;
drawSprite();
}
break;
case 87: // Up arrow was pressed
if(SpriteY > 0){
eraseSprite();
SpriteY--;
drawSprite();
}
break;
case 83: // Down arrow was pressed
if(SpriteY < 457) {
eraseSprite();
SpriteY++;
drawSprite();
}
break;
}
}
///]]>
</script>
<script type="application/javascript">
//
// Set up a function to handle key-presses.
//
// This should work across most browsers.
document['onkeydown'] = function(event) {
event = event || window.event;
var key = event.which || event.cursor;
// Check for a special key value, and map it to ASCII.
switch (key) {
case 119: // Left arrow, ASCII 29
key = 87;
break;
case 115: // Up arrow, ASCII 30
key = 83;
break;
case 100: // Right arrow, ASCII 28
key = 67;
break;
case 97: // Down arrow, ASCII 31
key =68;
break;
}
//document.getElementById("keydown").innerHTML =
// " key Down event, keycode " + key;
whenKeyPressed(key);
};
</script>
</body></html>
Your function whenKeyPressed is defined twice. As well as almost every other function on the page.
When the second instance of the function is parsed, your original version vanishes into thin air.
That’s why only one of them show up.
You’ll need to work on making one instance of the function work in both cases.
In the case of whenKeyPressed, just combine all of the cases in your switch statements into one switch statement and delete the second instance of the function.
You’ll need to do the same for the others.
The following is a rough guide and not tested at all, but will hopefully get you on the right path.
Your code at moment won't work as you are effectiveloy overwriting method and variables.
Here I've created a master object using JSON : sprites which contains an object for each sprites and maintains their states. The key mappings are associate with each sprite and can be expanded by adding to the DirectionKeyCodes propety on each of the sprite objects.
The active sprite is determined based on key press and is then passes to each of the worker functions.
var INTERVAL = 0;
var DEBUG = false; //true;
var canvas; // The canvas shown on the page.
var ctx; // The context, used to access the canvas.
let baseValues = {
"SpriteRow": 0, // Row of the graphic to show
"SpriteCol": 0, // Col of the graphic to show
"MaxSpriteRow": 8, // How many rows of images
"MaxSpriteCol": 8, // How many columns of images
"SpriteX": 100, // Position of sprite on the canvas
"SpriteY": 100,
"SpriteWidth": 32; // Width, Height of each subimage
"SpriteHeight": 32;
};
let pounceSprite = new Image();
pounceSprite.src = "Pounce.png";
let gsugSprite = new Image();
gsugSprite.src = "Gsug.png";
let sprites = {
"Pounce": {
"SpriteRow": baseValues.SpriteRow,
"SpriteCol": baseValues.SpriteCol, // Col of the graphic to show
"MaxSpriteRow": baseValues.MaxSpriteRow,
"MaxSpriteCol": baseValues.MaxSpriteCol, // How many columns of images
"SpriteX": baseValues.SpriteX, // Position of sprite on the canvas
"SpriteY": baseValues.SpriteY,
"SpriteWidth": baseValues.SpriteWidth, // Width, Height of each subimage
"SpriteHeight": baseValues.SpriteHeight,
"DirectionKeyCodes": {
"87": "UP",
"83": "DOWN",
"65": "LEFT",
"70": "RIGHT"
},
"Sprite": pounceSprite
}
},
"Gsug": {
"SpriteRow": baseValues.SpriteRow,
"SpriteCol": baseValues.SpriteCol, // Col of the graphic to show
"MaxSpriteRow": baseValues.MaxSpriteRow,
"MaxSpriteCol": baseValues.MaxSpriteCol, // How many columns of images
"SpriteX": baseValues.SpriteX, // Position of sprite on the canvas
"SpriteY": baseValues.SpriteY,
"SpriteWidth": baseValues.SpriteWidth, // Width, Height of each subimage
"SpriteHeight": baseValues.SpriteHeight,
"DirectionKeyCodes": {
"38": "UP",
"40": "DOWN",
"37": "LEFT",
"39": "RIGHT"
},
"Sprite": gsugSprite
}
}
// Set up a timer to execute every 50 ms.
var myInterval;
function eraseSprite(sprite) {
// erase sprite
ctx.clearRect(sprite.SpriteX, sprite.SpriteY, sprite.SpriteWidth, sprite.SpriteHeight);
}
function drawSprite(sprite) {
// draw sprite
//ctx.drawImage(SpriteImage, SpriteX, SpriteY);
ctx.drawImage(sprite.SpriteImage, sprite.SpriteCol * sprite.SpriteWidth, sprite.SpriteRow * sprite.SpriteHeight,
sprite.SpriteWidth, sprite.SpriteHeight, sprite.SpriteX, sprite.SpriteY, sprite.SpriteWidth, sprite.SpriteHeight);
// update the next image to show
// SpriteCol++;
if (sprite.SpriteCol >= sprite.MaxSpriteCol)
sprite.SpriteCol = 0;
}
function Tick() {
for (const sprite in sprites) {
// Erase the sprite from its current location.
eraseSprite(sprite);
// Show a new image
drawSprite(sprite);
}
}
function loadComplete() {
console.log("Load is complete.");
canvas = document.getElementById("theCanvas");
ctx = canvas.getContext("2d");
myInterval = self.setInterval(function() {
Tick()
}, INTERVAL);
}
// What to do when the user presses a key.
function whenKeyPressed(keyCode, sprite) {
switch (sprite.DirectionKeyCodes[keyCode]) {
case "RIGHT":
if (sprite.SpriteX < 612) {
eraseSprite(sprite);
sprite.SpriteX++;
drawSprite(sprite);
}
break;
case "LEFT":
if (sprite.SpriteX > 0) {
eraseSprite(sprite);
sprite.SpriteX--;
drawSprite(sprite);
}
break;
case "UP":
if (sprite.SpriteY > 0) {
eraseSprite(sprite);
sprite.SpriteY--;
drawSprite(sprite);
}
break;
case "DOWN":
if (sprite.SpriteY < 457) {
eraseSprite(sprite);
sprite.SpriteY++;
drawSprite(sprite);
}
break;
}
}
//
// Set up a function to handle key-presses.
//
// This should work across most browsers.
document['onkeydown'] = function(event) {
event = event || window.event;
var key = event.which || event.cursor;
//Work out what sprite we need
let sprite = null;
//Itteate the sprites in our object
for (const spriteTemp in sprites) {
//If it has a key mapping that matches the key press, we have our sprite
if (spriteTemp.DirectionKeyCodes.hasOwnProperty(key)) {
sprite = spriteTemp;
break;
}
}
//If we found a sprite
if (sprite !== null) {
//Send the key and sprite to the work method
whenKeyPressed(key, sprite);
}
}
I have been trying to add some html and javascript to my website so users can draw some shapes. I found a really good sample to work from on JS Fiddle. When I run the code on JSFiddle, it works perfectly, but when I ran it myself on my browser (I tried Edge, Firefox, and Chrome) it did not work.
When I ran it myself, I included all the scripts and css into one html file because thats the only way to add it to my Wix website. The scripts (local javascript, and external cdn libraries) where together in the body section of html. All the tutorials I found make it seem like it's OK to use the CDN libraries. I'm positive my issue has something to do with the connection to the CDN libraries, so how would I fix it?
Here is code:
var roof = null;
var roofPoints = [];
var lines = [];
var lineCounter = 0;
var drawingObject = {};
drawingObject.type = "";
drawingObject.background = "";
drawingObject.border = "";
function Point(x, y) {
this.x = x;
this.y = y;
}
$("#poly").click(function () {
if (drawingObject.type == "roof") {
drawingObject.type = "";
lines.forEach(function(value, index, ar){
canvas.remove(value);
});
//canvas.remove(lines[lineCounter - 1]);
roof = makeRoof(roofPoints);
canvas.add(roof);
canvas.renderAll();
} else {
drawingObject.type = "roof"; // roof type
}
});
// canvas Drawing
var canvas = new fabric.Canvas('canvas-tools');
var x = 0;
var y = 0;
fabric.util.addListener(window,'dblclick', function(){
drawingObject.type = "";
lines.forEach(function(value, index, ar){
canvas.remove(value);
});
//canvas.remove(lines[lineCounter - 1]);
roof = makeRoof(roofPoints);
canvas.add(roof);
canvas.renderAll();
console.log("double click");
//clear arrays
roofPoints = [];
lines = [];
lineCounter = 0;
});
canvas.on('mouse:down', function (options) {
if (drawingObject.type == "roof") {
canvas.selection = false;
setStartingPoint(options); // set x,y
roofPoints.push(new Point(x, y));
var points = [x, y, x, y];
lines.push(new fabric.Line(points, {
strokeWidth: 3,
selectable: false,
stroke: 'red'
}).setOriginX(x).setOriginY(y));
canvas.add(lines[lineCounter]);
lineCounter++;
canvas.on('mouse:up', function (options) {
canvas.selection = true;
});
}
});
canvas.on('mouse:move', function (options) {
if (lines[0] !== null && lines[0] !== undefined && drawingObject.type == "roof") {
setStartingPoint(options);
lines[lineCounter - 1].set({
x2: x,
y2: y
});
canvas.renderAll();
}
});
function setStartingPoint(options) {
var offset = $('#canvas-tools').offset();
x = options.e.pageX - offset.left;
y = options.e.pageY - offset.top;
}
function makeRoof(roofPoints) {
var left = findLeftPaddingForRoof(roofPoints);
var top = findTopPaddingForRoof(roofPoints);
roofPoints.push(new Point(roofPoints[0].x,roofPoints[0].y))
var roof = new fabric.Polyline(roofPoints, {
fill: 'rgba(0,0,0,0)',
stroke:'#58c'
});
roof.set({
left: left,
top: top,
});
return roof;
}
function findTopPaddingForRoof(roofPoints) {
var result = 999999;
for (var f = 0; f < lineCounter; f++) {
if (roofPoints[f].y < result) {
result = roofPoints[f].y;
}
}
return Math.abs(result);
}
function findLeftPaddingForRoof(roofPoints) {
var result = 999999;
for (var i = 0; i < lineCounter; i++) {
if (roofPoints[i].x < result) {
result = roofPoints[i].x;
}
}
return Math.abs(result);
}
.canvas {
border: 1px solid black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.4.0/fabric.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<button id="poly" title="Draw Polygon" ">Draw Polygon </button>
<label style="color:blue"><b>Press double click to close shape and stop</b></label>
<canvas id="canvas-tools" class="canvas" width="500" height="500"></canvas>
EDIT
So, in the html file I put everything inside the body tag. The libraries are also included before the javascript. I get the error "Unable to get property 'x' of undefined or null reference" when I double-click to close the shape. I'm positive its because no points are added when I click in the canvas
Wix does not allow using Cloudflare. The following link has more detail.
https://support.wix.com/en/article/request-cloudflare-support
Wix has some limited API to work with HTML elements
https://support.wix.com/en/article/corvid-working-with-the-html-element
if you want to run it on a separate page (not on wix) and scripts are loaded try to wrap your javascript code in :
<script>
$(function() {
//your code here
var roof = null;
var roofPoints = [];
var lines = [];
var lineCounter = 0;
var drawingObject = {};
...
...
});
</script>
I am trying to implement the handleOnPress function, the code is provided by the textbook which is confusing because it doesn't seem to be working as expected, in fact it does nothing.
The function is supposed to allow me to click the squares in the memory game, if the squares are the same color both of the chosen squares disappear, if you click on the same square twice the function resets and if you match two that aren't the same the function resets.
It would be really appreciated if anyone can take a look and point out any errors they see, thank you!
<!DOCTYPE html>
<html>
<head>
<title>Recipe: Drawing a square</title>
<script src="easel.js"></script>
<script type="text/javascript">
var canvas;
var stage;
var squareSide = 70;
var squareOutline = 5;
var max_rgb_color_number = 255;
var gray = createjs.Graphics.getRGB(20, 20, 20);
var placementArray = [];
var highlight = createjs.Graphics.getRGB(255, 255, 0);
var tileClicked;
function init() {
var rows = 6;
var columns = 6;
var squarePadding = 10;
canvas = document.getElementById('myCanvas');
stage = new createjs.Stage(canvas);
var numberOfTiles = rows*columns;
setPlacementArray(numberOfTiles);
for(var i=0;i<numberOfTiles;i++){
var placement = getRandomPlacement(placementArray);
if(i % 2 === 0){
var color = randomColor();
}
square = drawSquare(color);
square.color = color;
square.x = (squareSide+squarePadding) * (placement%columns);
square.y = (squareSide+squarePadding) * Math.floor(placement/columns);
stage.addChild(square);
square.onPress = handleOnPress;
stage.update();
}
}
function drawSquare(color) {
var shape = new createjs.Shape();
var graphics = shape.graphics;
graphics.setStrokeStyle(squareOutline);
graphics.beginStroke(gray);
graphics.beginFill(color);
graphics.rect(squareOutline, squareOutline, squareSide, squareSide);
return shape;
}
function randomColor(){
var color = Math.floor(Math.random()*max_rgb_color_number);
var color2 = Math.floor(Math.random()*max_rgb_color_number);
var color3 = Math.floor(Math.random()*max_rgb_color_number);
return createjs.Graphics.getRGB(color, color2, color3);
}
function setPlacementArray(numberOfTiles){
for(var i=0;i<numberOfTiles;i++){
placementArray.push(i);
}
}
function getRandomPlacement(placementArray){
randomNumber = Math.floor(Math.random()*placementArray.length);
return placementArray.splice(randomNumber, 1)[0];
}
function handleOnPress(event){
var tile = event.target;
if(!!tileClicked === false){
tile.graphics.setStrokeStyle(squareOutline).beginStroke(highlight)
.rect(squareOutline, squareOutline, squareSide, squareSide);
tileClicked = tile;
}
else{
if(tileClicked.color === tile.color && tileClicked !== tile){
tileClicked.visible = false;
tile.visible = false;
}
else{
tileClicked.graphics.setStrokeStyle(squareOutline).beginStroke(gray)
.rect(squareOutline, squareOutline, squareSide, squareSide);
}
tileClicked = null;
}
stage.update();
}
</script>
</head>
<body onload="init()">
<canvas id="myCanvas" width="960" height="600"></canvas>
</body>
</html>
The onEvent-style handlers (onClick, onMouseDown, etc) were deprecated and removed a long time ago (Version 0.7.0, September 25, 2013).
Instead, use events, such as
square.addEventListener("mousedown", handleOnPress);
or the shortcut on() method:
square.on("mousedown", handleOnPress);
Note also there is no "press" event. There is a "mousedown" event, as well as "pressmove"/"pressup" events for drag and drop-style events.
Here is a listing of events on all display objects: https://createjs.com/docs/easeljs/classes/DisplayObject.html#event_mousedown
I don't know if this will solve everything, but it should get your click handlers firing.
This is not a duplicate of the other question.
I found this talking about rotation about the center using XML, tried to implement the same using vanilla JavaScript like rotate(45, 60, 60) but did not work with me.
The approach worked with me is the one in the snippet below, but found the rect not rotating exactly around its center, and it is moving little bit, the rect should start rotating upon the first click, and should stop at the second click, which is going fine with me.
Any idea, why the item is moving, and how can I fix it.
var NS="http://www.w3.org/2000/svg";
var SVG=function(el){
return document.createElementNS(NS,el);
}
var svg = SVG("svg");
svg.width='100%';
svg.height='100%';
document.body.appendChild(svg);
class myRect {
constructor(x,y,h,w,fill) {
this.SVGObj= SVG('rect'); // document.createElementNS(NS,"rect");
self = this.SVGObj;
self.x.baseVal.value=x;
self.y.baseVal.value=y;
self.width.baseVal.value=w;
self.height.baseVal.value=h;
self.style.fill=fill;
self.onclick="click(evt)";
self.addEventListener("click",this,false);
}
}
Object.defineProperty(myRect.prototype, "node", {
get: function(){ return this.SVGObj;}
});
Object.defineProperty(myRect.prototype, "CenterPoint", {
get: function(){
var self = this.SVGObj;
self.bbox = self.getBoundingClientRect(); // returned only after the item is drawn
self.Pc = {
x: self.bbox.left + self.bbox.width/2,
y: self.bbox.top + self.bbox.height/2
};
return self.Pc;
}
});
myRect.prototype.handleEvent= function(evt){
self = evt.target; // this returns the `rect` element
this.cntr = this.CenterPoint; // backup the origional center point Pc
this.r =5;
switch (evt.type){
case "click":
if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true;
else self.moving = false;
if(self.moving == true){
self.move = setInterval(()=>this.animate(),100);
}
else{
clearInterval(self.move);
}
break;
default:
break;
}
}
myRect.prototype.step = function(x,y) {
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix().translate(x,y));
}
myRect.prototype.rotate = function(r) {
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix().rotate(r));
}
myRect.prototype.animate = function() {
self = this.SVGObj;
self.transform.baseVal.appendItem(this.step(this.cntr.x,this.cntr.y));
self.transform.baseVal.appendItem(this.rotate(this.r));
self.transform.baseVal.appendItem(this.step(-this.cntr.x,-this.cntr.y));
};
for (var i = 0; i < 10; i++) {
var x = Math.random() * 100,
y = Math.random() * 300;
var r= new myRect(x,y,10,10,'#'+Math.round(0xffffff * Math.random()).toString(16));
svg.appendChild(r.node);
}
UPDATE
I found the issue to be calculating the center point of the rect using the self.getBoundingClientRect() there is always 4px extra in each side, which means 8px extra in the width and 8px extra in the height, as well as both x and y are shifted by 4 px, I found this talking about the same, but neither setting self.setAttribute("display", "block"); or self.style.display = "block"; worked with me.
So, now I've one of 2 options, either:
Find a solution of the extra 4px in each side (i.e. 4px shifting of both x and y, and total 8px extra in both width and height),
or calculating the mid-point using:
self.Pc = {
x: self.x.baseVal.value + self.width.baseVal.value/2,
y: self.y.baseVal.value + self.height.baseVal.value/2
};
The second option (the other way of calculating the mid-point worked fine with me, as it is rect but if other shape is used, it is not the same way, I'll look for universal way to find the mid-point whatever the object is, i.e. looking for the first option, which is solving the self.getBoundingClientRect() issue.
Here we go…
FIDDLE
Some code for documentation here:
let SVG = ((root) => {
let ns = root.getAttribute('xmlns');
return {
e (tag) {
return document.createElementNS(ns, tag);
},
add (e) {
return root.appendChild(e)
},
matrix () {
return root.createSVGMatrix();
},
transform () {
return root.createSVGTransformFromMatrix(this.matrix());
}
}
})(document.querySelector('svg.stage'));
class Rectangle {
constructor (x,y,w,h) {
this.node = SVG.add(SVG.e('rect'));
this.node.x.baseVal.value = x;
this.node.y.baseVal.value = y;
this.node.width.baseVal.value = w;
this.node.height.baseVal.value = h;
this.node.transform.baseVal.initialize(SVG.transform());
}
rotate (gamma, x, y) {
let t = this.node.transform.baseVal.getItem(0),
m1 = SVG.matrix().translate(-x, -y),
m2 = SVG.matrix().rotate(gamma),
m3 = SVG.matrix().translate(x, y),
mtr = t.matrix.multiply(m3).multiply(m2).multiply(m1);
this.node.transform.baseVal.getItem(0).setMatrix(mtr);
}
}
Thanks #Philipp,
Solving catching the SVG center can be done, by either of the following ways:
Using .getBoundingClientRect() and adjusting the dimentions considering 4px are extra in each side, so the resulted numbers to be adjusted as:
BoxC = self.getBoundingClientRect();
Pc = {
x: (BoxC.left - 4) + (BoxC.width - 8)/2,
y: (BoxC.top - 4) + (BoxC.height - 8)/2
};
or by:
Catching the .(x/y).baseVal.value as:
Pc = {
x: self.x.baseVal.value + self.width.baseVal.value/2,
y: self.y.baseVal.value + self.height.baseVal.value/2
};
Below a full running code:
let ns="http://www.w3.org/2000/svg";
var root = document.createElementNS(ns, "svg");
root.style.width='100%';
root.style.height='100%';
root.style.backgroundColor = 'green';
document.body.appendChild(root);
//let SVG = function() {}; // let SVG = new Object(); //let SVG = {};
class SVG {};
SVG.matrix = (()=> { return root.createSVGMatrix(); });
SVG.transform = (()=> { return root.createSVGTransformFromMatrix(SVG.matrix()); });
SVG.translate = ((x,y)=> { return SVG.matrix().translate(x,y) });
SVG.rotate = ((r)=> { return SVG.matrix().rotate(r); });
class Rectangle {
constructor (x,y,w,h,fill) {
this.node = document.createElementNS(ns, 'rect');
self = this.node;
self.x.baseVal.value = x;
self.y.baseVal.value = y;
self.width.baseVal.value = w;
self.height.baseVal.value = h;
self.style.fill=fill;
self.transform.baseVal.initialize(SVG.transform()); // to generate transform list
this.transform = self.transform.baseVal.getItem(0), // to be able to read the matrix
this.node.addEventListener("click",this,false);
}
}
Object.defineProperty(Rectangle.prototype, "draw", {
get: function(){ return this.node;}
});
Object.defineProperty(Rectangle.prototype, "CenterPoint", {
get: function(){
var self = this.node;
self.bbox = self.getBoundingClientRect(); // There is 4px shift in each side
self.bboxC = {
x: (self.bbox.left - 4) + (self.bbox.width - 8)/2,
y: (self.bbox.top - 4) + (self.bbox.height - 8)/2
};
// another option is:
self.Pc = {
x: self.x.baseVal.value + self.width.baseVal.value/2,
y: self.y.baseVal.value + self.height.baseVal.value/2
};
return self.bboxC;
// return self.Pc; // will give same output of bboxC
}
});
Rectangle.prototype.animate = function () {
let move01 = SVG.translate(this.CenterPoint.x,this.CenterPoint.y),
move02 = SVG.rotate(10),
move03 = SVG.translate(-this.CenterPoint.x,-this.CenterPoint.y);
movement = this.transform.matrix.multiply(move01).multiply(move02).multiply(move03);
this.transform.setMatrix(movement);
}
Rectangle.prototype.handleEvent= function(evt){
self = evt.target; // this returns the `rect` element
switch (evt.type){
case "click":
if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true;
else self.moving = false;
if(self.moving == true){
self.move = setInterval(()=>this.animate(),100);
}
else{
clearInterval(self.move);
}
break;
default:
break;
}
}
for (var i = 0; i < 10; i++) {
var x = Math.random() * 100,
y = Math.random() * 300;
var r= new Rectangle(x,y,10,10,'#'+Math.round(0xffffff * Math.random()).toString(16));
root.appendChild(r.draw);
}
I have designed an HTML5 Game with a square that shoots at other squares. You have a certain amount of lives and gain a score. How do I prevent users from going into the console and doing something like this:
score=5000
planetHealth=200
Code to Game
$(document).ready(function() {
initStars(600);
});
var FPS = 60;
width = 300;
height = 400;
var gBackground = document.getElementById("canvas_background").getContext("2d");
var gPlayer = document.getElementById("canvas_player").getContext("2d");
var gEnemies = document.getElementById("canvas_enemies").getContext("2d");
var GUI = document.getElementById("canvas_ui").getContext("2d");
var bullets = [];
var enemies = [];
var shootTimer = 0;
var maxShootTimer = 15;
var score = 0;
var planetHealth = 50;
var gameState = "menu";
var Key = {
up: false,
down: false,
left: false,
right: false
};
var player = {
width: 16,
height: 16,
x: (width / 2) - 8,
speed: 3,
y: height - 20,
canShoot: true,
render: function() {
gPlayer.fillStyle="#24430A";
gPlayer.fillRect(this.x,this.y,this.width,this.height);
},
tick: function() {
if(Key.left && this.x > 0) this.x -= this.speed;
if(Key.right && this.x < width - 20) this.x += this.speed;
if(Key.space && this.canShoot) {
this.canShoot = false;
bullets.push(new Bullet(this.x,this.y - 4));
bullets.push(new Bullet(this.x + this.width,this.y - 4));
shootTimer = maxShootTimer;
}
}
};
stars = [];
addEventListener("keydown", function(e) {
var keyCode = (e.keyCode) ? e.keyCode : e.which;
switch(keyCode) {
case 38: // up
Key.up = true;
break;
case 40: // down
Key.down = true;
break;
case 37: // left
Key.left = true;
break;
case 39: // right
Key.right = true;
break;
case 32: //spacebar
Key.space = true;
break;
}
}, false);
addEventListener("keyup", function(e) {
var keyCode = (e.keyCode) ? e.keyCode : e.which;
switch(keyCode) {
case 38: // up
Key.up = false;
break;
case 40: // down
Key.down = false;
break;
case 37: // left
Key.left = false;
break;
case 39: // right
Key.right = false;
break;
case 32: //spacebar
Key.space = false;
break;
}
}, false);
function collision(obj1,obj2) {
return (
obj1.x < obj2.x+obj2.width &&
obj1.x + obj1.width > obj2.x &&
obj1.y < obj2.y+obj2.height &&
obj1.y + obj1.height > obj2.y
);
}
function Star(x,y) {
this.x = x;
this.y = y;
this.size = Math.random() * 2.5;
this.render = function() {
gBackground.fillStyle = "white";
gBackground.fillRect(this.x,this.y,this.size,this.size)
};
this.tick = function() {
this.y++;
}
}
function createStars(amount) {
for(i=0;i<amount;i ++) {
stars.push(new Star(Math.random() * width, -5));
}
}
function initStars(amount) {
for(i=0;i<amount;i++) {
stars.push(new Star(Math.random()*width,Math.random()*height));
}
}
function Bullet(x,y) {
this.x = x;
this.y = y;
this.width = 2;
this.height = 12;
this.speed = 3;
this.render = function() {
gPlayer.fillStyle = "red";
gPlayer.fillRect(this.x,this.y,this.width,this.height);
};
this.tick = function() {
if(this.y < -this.height) {
var index = bullets.indexOf(this);
bullets.splice(index,1);
}
this.y-=this.speed;
for(i in enemies) {
if(collision(this,enemies[i])) {
score = score + 50;
GUI.clearRect(0,0,width,height);
GUI.fillStyle ="white";
GUI.textBaseline = "top";
GUI.font = "bold 14px Tahoma";
GUI.fillText("Score: " + score, 2,2);
GUI.fillText("Lives: " + planetHealth, 2,16);
var enemyIndex = enemies.indexOf(enemies[i]);
enemies.splice(enemyIndex,1);
var bulletIndex = bullets.indexOf(this);
bullets.splice(bulletIndex,1);
}
}
};
}
function Enemy(x,y) {
this.x = x;
this.y = y;
this.width = 16;
this.height = 16;
this.speed = 0.5;
;
this.render = function() {
gEnemies.fillStyle = "red";
gEnemies.fillRect(this.x,this.y,this.width,this.height);
};
this.tick = function() {
if(this.y > this.height + height) {
this.y = -this.height;
planetHealth--;
GUI.clearRect(0,0,width,height);
GUI.fillStyle ="white";
GUI.textBaseline = "top";
GUI.font = "bold 14px Tahoma";
GUI.fillText("Score: " + score, 2,2);
GUI.fillText("Lives: " + planetHealth, 2,16);
}
this.y += this.speed;
}
}
for(x=0;x<8;x++) {
for(y=0;y<8;y++) {
enemies.push(new Enemy((x*24)+(width/2)-100,y*24));
}
}
function render() {
if(gameState == "play") {
gBackground.clearRect(0,0,width,height);
gPlayer.clearRect(0,0,width,height);
gEnemies.clearRect(0,0,width,height);
player.render();
for(i in stars) {
stars[i].render();
}
for(i in enemies) enemies[i].render();
for(i in bullets) bullets[i].render();
} else if(gameState == "gameOver") {
gBackground.clearRect(0,0,width,height);
for(i in stars) {
stars[i].render();
}
GUI.fillStyle = "white";
GUI.font = "bold 24px Tahoma";
GUI.fillText("You're a loser!", width / 2 - 100, height/2);
gEnemies.clearRect(0,0,width,height);
gPlayer.clearRect(0,0,width,height);
} else if(gameState == "gameWin") {
gBackground.clearRect(0,0,width,height);
for(i in stars) {
stars[i].render();
}
GUI.fillStyle = "white";
GUI.font = "bold 24px Tahoma";
GUI.fillText("You're a winner!", width / 2 - 100, height/2);
gEnemies.clearRect(0,0,width,height);
gPlayer.clearRect(0,0,width,height);
} else if(gameState == "menu") {
gBackground.clearRect(0,0,width,height);
for(i in stars) {
stars[i].render();
}
GUI.fillStyle = "white";
GUI.font = "bold 24px Tahoma";
GUI.fillText("Space Game!", width / 2 - 100, height/2);
GUI.font= "normal 16px Tahoma";
GUI.fillText("Press space to start", width / 2 - 90, (height/2)+28);
}
}
if(gameState == "play") {
GUI.fillStyle ="white";
GUI.textBaseline = "top";
GUI.font = "bold 14px Tahoma";
GUI.fillText("Score: " + score, 2,2);
GUI.fillText("Lives: " + planetHealth, 2,16);
}
function tick() {
createStars(1);
for(i in stars) stars[i].tick();
if(gameState == "play") {
if(planetHealth <= 0) gameState = "gameOver";
if(enemies.length <= 0) gameState = "gameWin";
player.tick();
for(i in enemies) enemies[i].tick();
for(i in bullets) bullets[i].tick();
if(shootTimer <= 0) player.canShoot = true;
shootTimer--;
} else if(gameState == "menu") {
if(Key.space) {
gameState = "play";
GUI.clearRect(0,0,width,height);
}
}
}
setInterval(function() {
render();
tick();
}, 1000/FPS );
<!DOCTYPE html>
<html>
<head>
<title> Game </title>
<style>
canvas {
position: absolute;
top: 0;
left: 0;
}
#canvas_background {
background: black;
}
</style>
</head>
<body>
<canvas id='canvas_background' width='300' height='400'></canvas>
<canvas id='canvas_player' width='300' height='400'></canvas>
<canvas id='canvas_enemies' width='300' height='400'></canvas>
<canvas id='canvas_ui' width='300' height='400'></canvas>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
<script src='game.js'></script>
</body>
</html>
You can't prevent a user from tampering with the console and the browser's dev tools gives you a lot of ways to take a peek anywhere in the code, even in closures as well as minified code.
But... you could make it harder.
First, you could do it like Facebook and just print a big red warning in the console saying "You shouldn't be here". We're essentially just scaring off the user, really.
Another option is to wrap the code in a closure, that way it's not exposed in the global scope. This avoids direct manipulation via the console.
;(function(){
// all code in here
());
Making it a bit harder is to use a minifier and an obfuscator.
The main purpose of a minifier is to shave file size by renaming names and rewriting code in a shorter way. The side effect is that the code becomes hard to read as most of the time it won't have any resemblance to your original code. It's worse without a source map and may take hours to trace and understand.
An obfuscator rewrites your code in a way that it still runs the same, just written in a different and often non-readable way. They even go as far as encoding the rewritten code in base64. For those who don't know what base64 is, they're good as gone.
Again, we're just making your code a bit harder to reach, fending off wannabe "hackers".
A more fool-proof way would be to just validate off-page, like on the server and use a variety of methods to determine tampered data. Games like speed typing impose a max score at a certain length of time since we all know we can't type a million words a second. Some games involve data analysis, if the data looks out of the ordinary.
You don't. If your game is entirely client-side, then you can't really stop players from cheating. You could make it more difficult with code obfuscation or taking variables out of the global scope, but that won't stop people who really want to cheat.
If players are connecting to a server for multiplayer or whatever you could implement server-side checks, since the users won't be able to touch that code.
You're running your code on the main scope of the Javascript, which is window.
When you create a global varable, this variable is scoped in the window object.
For that reason you can do this:
var a = 1;
console.log(a); // 1
console.log(window.a); // 1
It's very easy to avoid this, using the famous IIFE, which stands for Immediately Invoked Function Expression. You can read it at MDN.
Is just do this:
(function() {
// put all your code inside here
})();
When you do this, all the variable decladed inside that function, will be contained to the scope of that function.
But be aware, you can't prevent user cheating the game, you can only make it harder.