Adobe Animate Canvas Parent Arrays? - javascript

I have a game I'm making in adobe animate HTML canvas. In my code below Is there a way to combine all of the stateItems[].stateplace1 =this.state so I don't have like 50 different variations? I tried make the state1s the child of the StateItems but it still feels very derivative.
If stateItems and state1s were two different arrays is there a way to call the first item (Florida) of stateItems to the first target(state1)and so forth so they are "linked" so speak. Would something like that work and how would I go about do that?
I'm very new to javascript so my apologies if there is a super easy solution.
Edit:
The StateItems are individual symbols in Animate of the actual United States of America example: Florida is a state in the literal United States so it's part of my StateItem array. The stateplace is where the states will be placed and stateplace is one symbol and stateplace 1 2 and 3 are instances of stateplace. I just wanted to know if there was a way to cleanup the stateplace1s in the StateItems loop so there isn't 50 stateItems[0].stateplace1 = this.stateplace1;
I just couldn't figure out another way to connect each StateItem to its corresponding stateplace. The code works the way I need it to I just don't know if theres a way to clean it up a bit. I hope that clears things up.
var stateItems = [this.florida, this.alabama, this.southcarolina]
// we apply the same code for each symbol with the for loop
for(var i = 0; i<stateItems.length; i++){
stateItems[i].on("mousedown", onMouseDown.bind(this));
stateItems[i].on("pressmove", onMouseMove.bind(this));
stateItems[i].on("pressup", onMouseUp.bind(this));
stateItems[0].stateplace1 = this.stateplace1;
stateItems[1].stateplace1 = this.stateplace2;
stateItems[2].stateplace1 = this.stateplace3;
stateItems[i].originX = stateItems[i].x;
stateItems[i].originY = stateItems[i].y;
}
// mouse down event
function onMouseDown(evt){
var item = evt.currentTarget;
item.offset = {x:0, y:0};
var pt = item.parent.globalToLocal(evt.stageX, evt.stageY);
item.offset.x = pt.x - item.x;
item.offset.y = pt.y - item.y;
item.drag = true;
}
// mouse up event
function onMouseUp(evt){
var item = evt.currentTarget;
item.drag = false;
var pt = item.localToLocal(item.dot.x, item.dot.y, item.state1.hitBox);
if(item.stateplace1.hitBox.hitTest(pt.x, pt.y) ){
item.x = item.stateplace1.x;
item.y = item.stateplace1.y;
item.mouseEnabled = false; // prevents object from being move when place correctly
}else{
item.x = item.originX;
item.y = item.originY;
}
}
function onMouseMove(evt){
var item = evt.currentTarget;
if (item.drag){
var pt = item.parent.globalToLocal(evt.stageX, evt.stageY);
item.x = pt.x - item.offset.x;
item.y = pt.y - item.offset.y;
}
// mouse move event
}

Related

Weird behavior with parameters on a ES6 Class

I have a class, which contains a parameter called values. This is used to save values of points that represent specific shapes on a canvas.
I need to implement a functionality that lets me drag those shapes around, so I need to modify each specific point of the shape, removing from them the amount that was dragged.
So I decided that, as I trigger my mousedown event (which is the method StartMove), i would save the values of my points on a startValues variable, and as I move my mouse around (method move), I would then update the values, using startValues and the distance between the starting point and the current mouse position to determine my new point location.
The problem is, this.startValues is actually getting changed to match this.values every time my cursor moves, and I have no idea why. Is there anything simple I'm missing?
Since I store my values as values, and not coordinates (helps me with panning and zooming on the canvas), I first convert the values to position, then modify the position and then convert it back to a value. I've included the parent class, Grf, so you can see the methods which change values to position and position to values.
Class with the problems
class Test {
constructor(grf){
this.grf = grf; // Another class, which contains important methods
this.values = [];
this.startValues = [];
}
startMove(p0){ // p0 = [x,y]
const {grf} = this;
this.startValues = [...this.values]; //I also tried this.startValues = this.values
this.p0 = p0;
grf.canvas.addEventListener('mousemove',this.move);
grf.canvas.addEventListener('mouseup', this.endMove);
}
move = (evt) => { // arrow function so 'this' is bound to Test class instead of grf.canvas
const {grf, p0, values, startValues} = this;
const coords = grf.canvas.getBoundingClientRect();
const px = evt.clientX - coords.left;
const py = evt.clientY - coords.top;
for (let i = 0, iMax = this.values.length; i < iMax; i++){
values[i][0] = grf.valX(grf.posX(startValues[0]) - (p0[0] - px));
values[i][1] = grf.valY(grf.posY(startValues[1]) - (p0[1] - py));
}
console.log(this.startValues); // It changes to the same as this.values
}
endMove = (evt) => { // arrow function so 'this' is bound to Test class instead of grf.canvas
const {grf} = this;
grf.canvas.removeEventListener('mousemove',this.move);
grf.canvas.removeEventListener('mouseup',this.endMove);
}
}
The other class
class Grf {
constructor(canvas){ // Not the actual constructor, just an example of what the values could look like
this.translateX = 1000;
this.translateY = 1000;
this.scaleY = 10.7;
this.scaleX = 11.2;
this.canvas = canvas;
}
posX (value){
return (value-this.translateX)*this.scaleX;
}
posY (value){
return (this.canvas.height-(100*(value))-this.translateY)*this.scaleY;
};
valX(pos){
return (pos/this.scaleX) + this.translateX
}
valY(pos){
return (-1)*((pos/this.scaleY) + this.translateY - this.canvas.height)/100
}
}
How values are inserted into startValues and values in Test class? You probably insert exactly the same object in both without coping it so both arrays hold the same instances.
Take a look at the example:
const obj = { a : 10 };
const a = [];
a.push(obj);
const b = [...a]; // creates new array, but with same objects
a[0].a = 20;
console.log(b[0]) // gives "{ a : 20 }"
To make it separate you need to make a copy of a object:
a.push({...obj})

Program not entering into for-loop

I'm currently in a Computer Science class at my high school and am writing a program to simulate Conway's Game of Life. I am writing the program in the Code Studio "App Lab" which uses JavaScript and is what we have been learning in. It has a smartphone on the left which you design for.
It's been going all fine and dandy so far, but I am working on trying to draw the cells on the screen and my program refuses to enter into the for loop which will draw the cells (which are represented as buttons). The function which draws the CellBoard, is called drawBoard and is a method inside of the CellBoard object.
function Cell(x, y, id) {
//base unit for the program, can be either dead or alive based on Conway's
//Game of Life Rules
this.xPos = x;
this.yPos = y;
this.id = "cell" + id;
this.alive = false;
this.aliveNextTurn = false;
this.aliveNeighbors = 0;
this.age = 0;
this.swapState = function(){
if(this.alive){
this.alive = false;
}
else{
this.alive = true;
}
};
}
function CellBoard(width, height){
//the board of cells, this object will house all the methods for the rule
//checking and state setting
this.board = [];
var count = 0;
for(var x = 0; x<width; x++){
var boardY =[];
for(var y = 0; y<height; y++){
boardY.push(new Cell(x,y,count));
count++;
}
this.board.push(boardY);
}
this.drawBoard = function(){
//draws the board of cells on the screen as buttons so that the user can
//initially set them
setScreen("simulationScreen");
//console.log("screen set");
//console.log("starting button making");
for(var i = 0; i<this.width; i++){ //<----the problem is here
//console.log("starting loop");
for(var j = 0; j<this.height; j++){
//console.log("making button");
button(this.board[i][j].id, "test");
setPosition(this.board[i][j].id, 20+(280/i), 20+(280/j), 280/i, 280/j);
setProperty(this.board[i][j].id, "background-color", rgb(0,0,0)); //black background by default
//console.log(getProperty(this.board[i][j].id, "x"));
}
}
//console.log("done drawing board");
};
}
var testBoard = new CellBoard(3, 3);
testBoard.drawBoard();
Any help is greatly appreciated, thanks!
Here are the console logs from the function that has the problem:
screen set
starting button making
done drawing board
looks like in your for loop in the drawBoard function, you use this.width and this.height. However you never set this.width and this.height. In the initialization code in the CellBoard class, you should be setting this.width = width. It might be skipping the for loop because this.width is undefined which doesn't satisfy the for loop condition.
As well, you use the this keyword in your drawBoard function. Within a function like that, this will refer to the function and not the object. Instead in the initialization code, you may want to create a variable to hold this. You can do cell_board = this in the initialization code and then use cell_board.width instead in the drawBoard function.

shuffling cards with Javascript and inconsistent array values?

I'm building a little module in javascript to act like a pack of cards. My first method works but was quite simple, and so i wanted to create some shuffle methods that mimic the idea behind real world card shuffling.
Amongst some other useful functions I've create riffle, overhand and cut functions, that all seem to do there job, but when calling them repeatedly in sequence the returned pack amount is inconsistent, from running it over and over again it appears to be some sort of race condition, but can't seem to get my head around how to avoid it.
The relevant private methods are :
riffle : function riffle() {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log('top is '+top.length+" and bottom is "+bottom.length);
var hand = 'right';
var result = [];
var i = 52;
while (i > 0) {
var drop = Math.floor(Math.random()*3)+1;
var cards;
if (hand === 'right' ) {
if (drop >= top.length) {
cards = top;
} else {
cards = top.splice(0, drop);
}
hand = 'left';
} else {
if (drop >= bottom.length) {
cards = bottom;
} else {
cards = bottom.splice(0, drop);
}
hand = 'right';
}
result = result.concat(cards);
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
cut : function cut(fn) {
var top = Pack.slice(0, 26);
var bottom = Pack.slice(26, 52);
Pack = [];
console.log(top);
Pack = bottom.concat(top);
console.log(Pack.length+" after cut");
if (fn && typeof(fn) === 'function') { fn(); }
return this;
}
Later on I have a privileged method called shuffle that calls them :
shuffle : function shuffle(cb) {
State.cardsOut = [];
Internal.generatePack().cut().riffle().riffle()
.riffle().riffle().riffle();
if (cb && typeof(cb) === 'function') { cb(); }
}
Note : I start with a generate function that creates an arrray of objects representing a full pack of 52 cards. The results I get when I console log the pack at different times after shuffles and cuts vary and I can't seem to figure out why.
you can see what i'km working on here
https://gist.github.com/Pushplaybang/66bc7a1fa5d84eee2236
Any help would be awesome.
The drop variable stores the number of cards you are supposed to be riffling from either the left or right hand. However, there are two instances:
if (drop >= top.length) {
cards = top;
}
and
if (drop >= bottom.length) {
cards = bottom;
}
where drop can be greater than the number of remaining cards in the half of the pack so more cards will be subtracted from i than you have actually riffled. You can fix this by:
if (drop >= top.length) {
drop = top.length;
cards = top;
top = [];
}
and
if (drop >= bottom.length) {
drop = top.length;
cards = bottom;
bottom = [];
}
(You need to empty the arrays or you may end up adding the same cards twice).
Other issues
You have magic numbers in the code (26 and 52) these could be constants defined in the class and given appropriate names (i.e. PACK_SIZE = 52) which would mean that if you create a sub-class representing a different number of cards then it would still work.
hand has two possible values which could be represented as a boolean but you assign it strings (again you could use constants LEFT_HAND = true, RIGHT_HAND = !LEFT_HAND).
Pack appears to be a global variable - I would have thought it ought to be a member of the class.
You do not need to name the functions as this is just polluting the global namespace: riffle : function riffle() { can just be an anonymous function riffle : function() {.
Performance - you create additional arrays with each iteration and the cards are moved multiple times. This could be more efficient.
Something like this:
PACK_SIZE: 52,
riffle : function() {
var index_of_cards_riffled_from_top = 0;
var index_of_cards_riffled_from_bottom = this.PACK_SIZE / 2;
var riffled_cards = [];
while ( index_of_cards_riffled_from_top < this.PACK_SIZE / 2
|| index_of_cards_riffled_from_bottom < this.PACK_SIZE ) {
var num_cards_to_riffle_top = Math.min( this.PACK_SIZE / 2 - index_of_cards_riffled_from_top, Math.floor( Math.random() * 3 ) + 1 );
var num_cards_to_riffle_bottom = Math.min( this.PACK_SIZE - index_of_cards_riffled_from_bottom, Math.floor( Math.random() * 3 ) + 1 );
while ( num_cards_to_riffle_top > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_top++ ] );
num_cards_to_riffle_top--;
}
while ( num_cards_to_riffle_bottom > 0 ) {
riffled_cards.push( this.Pack[ index_of_cards_riffled_from_bottom++ ] );
num_cards_to_riffle_bottom--;
}
}
this.Pack = riffled_cards;
}
while #MTO 's answer did solve my problem, I'd like to shed some light on how I've chosen to begin refactoring this function.
riffle : function riffle() {
var cutPos = Math.floor(Math.random()*rv)+( (cardCount-rv) / 2 );
var splitPack = {
left : Pack.splice(0, cutPos),
right : Pack.splice(0, Pack.length)
};
var hand = 'right',result = [], i = 52, cards;
while(i > 0) {
drop = Math.floor(Math.random()*3)+1;
if (drop >= splitPack[ hand ].length) {
drop = splitPack[ hand ].length;
}
cards = splitPack[ hand ].splice(0, drop);
hand = (hand === 'left') ? 'right' : 'left';
result = result.concat(cards);
cards = [];
i -= drop;
}
Pack = result;
console.log(Pack.length+" after riffle");
return this;
},
a few things :
the elements that seem global are not really, as this is all wrapped within a function that creates a new "deck" object, and some elements need to be private, such as the cards remaining in the pack once dealing has begin.
While booleans would work well for the hands, I wanted to boil this down somewhat and so use the strings to select obj properties.
everything MTO said about using constants is absolutely valid.
by now splicing each time, we're removing the elements from the array.
I prefer this approach as it only uses one while loop.
lastly, this type of shuffle is meant to emulate hand shuffling, and must be combined with other hand shuffling methods, ideally in a repetitive sequence, to produce something useful,
if you want something consistently random and efficient use fischer-yates algorithm.

Value not getting added to array?

Simple question which seems impossible for me because I'm just staring in the code.
Basicly I have this function, I call it X amount of times and it should put all the created divs in a array called world which I've declared outside of the function.
However, if I try to use one of these values they are "undefined".
var world = [];
function newTile(x, y, size, rotX, rotY, rotZ, tranX, tranY, tranZ, color) {
var tile = document.createElement('div');
tile.className = "tile";
tile.style.width = size+"px";
tile.style.height = size+"px";
tile.style.webkitTransform =
"rotateX("+rotX+"deg)"+
"rotateY("+rotY+"deg)"+
"rotateZ("+rotZ+"deg)"+
"translateX("+tranX+"px)"+
"translateY("+tranY+"px)"+
"translateZ("+tranZ+"px)";
tile.style.transform =
"rotateX("+rotX+"deg)"+
"rotateY("+rotY+"deg)"+
"rotateZ("+rotZ+"deg)"+
"translateX("+tranX+"px)"+
"translateY("+tranY+"px)"+
"translateZ("+tranZ+"px)";
if (x == 0 && y == 0) {
color="rgba(255,255,0,0.5)";
pathStart = tile;
pathCur = tile;
}
tile.style.backgroundColor = color;
tile.data = {
x:x,
y:y,
blacklist:0
}
tile.onclick = function() {
worldOri(null,null,null, -this.data.x*128 - 64, null, -this.data.y*128 - 64);
};
if (debug) tile.textContent = x+", "+y;
document.getElementById('world').appendChild(tile);
world[x] = [];
world[x][y] = tile;
}
Lets say I do something like:
newTile(2,6,128,90,0,0,2*128,0,6*128, "rgba(255,125,0,0.5)");
This works as intended and surely creates a div, placing it "in" another div with the id "world" and SHOULD add the div to the array "world" at [2][6]. If I now try to do something with the div, for example change color:
world[2][6].style.backgroundColor = "rgba(255,0,0,0.5)";
It returns as undefined, which I assume is that the actual adding to the "world" array didn't work, please help.
world[x] = []; will assign an empty array world[x] every time you make a call to newTile, thus "removing" all existing tiles from world[x]. Only initialize it if it doesn't exist yet:
world[x] = world[x] || [];

Javascript stop repeating collision detection in canvas

I'm trying to do some simple collisions in javascript and html5 canvas. So far I have this:
checkCollision = (function(){
var l, i, ni,dis = 0;
var ob1,ob2 = {};
return function(){
//collisions is the array holding all the objects
l = collisions.length;
i = 0;
ni = 1;
while(i<l){
//grab the first object
ob1 = collisions[i];
while(ni<l){
//get the object to check against
ob2 = collisions[ni];
//find the distance between the two
dis = Math.sqrt(Math.pow((ob1.pos[0]-ob2.pos[0]),2)+Math.pow((ob1.pos[1]-ob2.pos[1]),2));
//rad is the radius
if(ob1.rad+ob2.rad >= dis){
console.log("collision")
}
//move forward second increment
ni++;
}
i++;
//keep one place ahead
ni=i+1;
}
};
})();
I did it without any help of any kind, but now I guess my brain is too much mush to figure this last part out. The collision is happening every frame, which I don't want. I just want it to fire once when the collision first happens. I've tried by giving each object a collide variable that's true if there's already a collision but it's not working very well. Some it fires once and some it fires constantly.
Does anyone have any pointers?
What do you mean by "happening every frame"? The algorithm seems ok, but there doesn't seem to be any conesquence from the collision, it is just logged. Do you want to break out of the loop?
Some comments that have nothing to do with your issue but might make your code a bit more readable and concise:
> checkCollision = (function(){
> var l, i, ni,dis = 0;
> var ob1,ob2 = {};
I don't know why you initialise dis and ob2, they are assigned values later. Using a closure like this means that the values persist to subesquent calls until new values are assigned. Is the closure really needed? It may be a performance hit for animations like this.
> ni = 1;
> while(i<l){
You can put ni after the while, like this:
while(i < l){
ni = i + 1;
and get rid of the last ni = i + 1. You can also do:
> ob1 = collisions[i++];
and get rid of the last i++;, an do the same for ni when assigning to ob2.
Doing the above reduces the number of lines of code so it is more digestable, e.g.
function checkCollision() {
var ni, dis, ob1, ob2;
//collisions is the array holding all the objects
var l = collisions.length;
var i = 0;
while (i < l) {
ni = i + 1;
//grab the first object
ob1 = collisions[i++];
while (ni < l) {
//get the object to check against
ob2 = collisions[ni++];
//find the distance between the two
dis = Math.sqrt(Math.pow((ob1.pos[0] - ob2.pos[0]), 2) +
Math.pow((ob1.pos[1] - ob2.pos[1]), 2));
//rad is the radius
if (ob1.rad + ob2.rad >= dis) {
console.log("collision");
// And what else?
}
}
}
}

Categories