javascript game sprite positioning - javascript

I'm trying to create a chess board, and place it in the middle of the screen, so far i cannot get it to be directly in the center. i don't want to hard code the position to the screen because i'm going to be dealing with different screen sizes.
var winsize = cc.director.getWinSize();
var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
for (i=0; i<64; i++){
var tile = cc.Sprite.create(res.tile_png);
this.addChild(tile,0);
tile.setPosition(winsize.width+i%8*50/-10, winsize.height-Math.floor(i/8)*50);
}
But the tiles and positioning is completely off

#jumpman8947, if you're using Cocos2d js perhaps you have a similar line: cc.view.setDesignResolutionSize(480, 320, cc.ResolutionPolicy.SHOW_ALL);
In this particular case the game will scale to any sceeen, but still run in 480x320 resolution, so no matter what screen resoultion you use, the center in the cocos world would always be cc.p(240, 160) so no matter what's the window size or the screen resolution, the resolution of the game stays the same
You can read more about resolution policies here (and in official js-doc):
http://www.cocos2d-x.org/wiki/Multiple_Resolution_Policy_for_Cocos2d-JS
Also please be aware, that the Sprite position in Cocos is the position of the centre of the sprite, not bottom left corner

In your question it's not completely clear exactly what you want. However, I made some assumptions. The explanation for my solution is embedded in the comments in the code below.
// var winsize = cc.director.getWinSize();
// Here is some example hard-coded return values:
var winsize = {width: 600, height: 400};
// You can change these numbers to see how they influence
// the outcome.
// var centerpos = cc.p(winsize.width / 2, winsize.height / 2);
// This line doesn't seem relevant for the question you asked.
// Or, rather, the following calculations will result in the tiles
// being centred on the screen anyway, so this calculation here
// is unnecessary.
// Being a chess board, I assume that you want the tiles to be square,
// i.e. to have the same width and height.
// If so, first find out which is the minimum dimension
// and calculate the tile size as being 1/8 of that.
var minDimn = Math.min(winsize.width, winsize.height);
var tileSize = minDimn / 8;
// Find out how far in from the left and how far down from the top
// you need the upper left corner of the upper left tile to start.
// This assumes that you don't need any "margin" around the board.
// (If you do need such a "margin", basically subtract it twice
// from each of winsize.width and winsize.height above.)
// Start with default values of 0 for each, but then add in the
// excess for the longer dimension, but divide it by two
// because that excess will be split between either
// the top and bottom or the left and right.
var xStart = 0, yStart = 0;
if (winsize.width > winsize.height) {
xStart = (winsize.width - winsize.height) / 2;
} else if (winsize.height > winsize.width) {
yStart = (winsize.height - winsize.width) / 2;
}
// Instead of looping through all 64 positions in one loop,
// loop through all the horizontal positions in an outer loop
// and all the vertical positions in an inner loop.
for (i = 0; i < 8; i++) {
// For the horizontal dimension, calculate x for each tile
// as the starting position of the left-most tile plus
// the width of the tile multiplied by the number of tiles (0-based)
var x = xStart + i * tileSize;
// Now the inner loop
for (j = 0; j < 8; j++) {
// Same type of calculation for the y value.
var y = yStart + j * tileSize;
// You can see the values in this demo here.
document.write("<pre>(" + x + ", " + y + ")</pre>");
// The following two lines don't seem to be relevant to the question.
// var tile = cc.Sprite.create(res.tile_png);
// this.addChild(tile,0);
// Use your calculated values in your function call.
// tile.setPosition(x, y);
}
}

Related

PIXI Endlessly move sprite over the scene (show at the top as much as hide in bottom)

There is endlessly moving sprite "green block" from top to bottom and it works. Is it possible to show sprite moving like "around" the stage show at the top as much as hide in bottom. I don't know exactly how this effect can be called, but I mean when green block is starting to move down the scene border, then start showing it again at the top. How can it be done and can you, please, show how to do this?
const WIDTH = 500;
const HEIGHT = 500;
const app = new PIXI.Application({
width: WIDTH,
height: HEIGHT,
backgroundColor: 0x000000
});
document.body.appendChild(app.view);
const sprite = PIXI.Sprite.from('https://i.ibb.co/b3Sjn6M/greeenblock.png');
sprite.width = 100;
sprite.height = 100;
// Center
sprite.anchor.set(0.5);
sprite.x = app.screen.width / 2;
sprite.y = app.screen.height / 2;
app.stage.addChild(sprite);
// Listen for animate update
app.ticker.add((delta) => {
// Move from topto bottom
sprite.position.y += delta * 2;
if (sprite.position.y > HEIGHT + sprite.height / 2) {
sprite.position.y = -sprite.height / 2;
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.3.3/pixi.min.js"></script>
Solution (with flickering) provided by #Blindman67:
const WIDTH = 500;
const HEIGHT = 500;
const app = new PIXI.Application({
width: WIDTH,
height: HEIGHT,
backgroundColor: 0x000000
});
document.body.appendChild(app.view);
const sprite = PIXI.Sprite.from('https://i.ibb.co/b3Sjn6M/greeenblock.png');
const spriteReverse = PIXI.Sprite.from('https://i.ibb.co/b3Sjn6M/greeenblock.png');
sprite.width = 100;
sprite.height = 100;
spriteReverse.width = 100;
spriteReverse.height = 100;
// Center
sprite.anchor.set(0.5);
sprite.x = app.screen.width / 2;
sprite.y = app.screen.height / 2;
spriteReverse.anchor.set(0.5);
spriteReverse.x = app.screen.width / 2;
spriteReverse.y = app.screen.height / 2;
app.stage.addChild(sprite);
app.stage.addChild(spriteReverse);
let y = 0;
// Euqlidian modulo
const modAbs = (value, modulo) => (value % modulo + modulo) % modulo;
// Listen for animate update
app.ticker.add((delta) => {
// Move from topto bottom
y += delta * 2;
if (y > HEIGHT + sprite.height / 2) {
y = -sprite.height / 2;
}
// use modulo to warp
y = modAbs(y, HEIGHT);
// check if sprite overlaps the screen edge
spriteReverse.visible = false;
if (y + sprite.height > HEIGHT) { // is crossing then
spriteReverse.visible = true;
spriteReverse.position.y = (y - HEIGHT) // ... draw a copy at opposite edge.
}
sprite.position.y = y
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/5.3.3/pixi.min.js"></script>
If I understand you: you have one box which you wish to move in an infinite loop from the top to the bottom. Once it hits the bottom it should start showing at the top.
The easiest way I can think of would be to have two identical boxes.
Both starts at the top and only one moves down. Once it hits the bottom the other box can start moving down.
When the first box is completely off-screen you reset it's position.
And repeat.
% Remainder operator
This can be done using the remainder operator %
For example if the screen is 1000 pixels wide and you have a coordinate of 1500, that is the object has warped around the screen 1.5 times, using the remainder operator 1500 % 1000 = 500.
If only moving in a positive direction then this is all that is needed (apart from popping)
x = x % screenWidth;
// and/or for y
y = y % screenHeight;
Negative space
However there is a problem if the object moves in the other direction as the remainder operation keeps the sign of the number -1500 % 1000 === -500, and even worse if you use Math.abs on the result you still get the wrong value Math.abs(-1200 % 1000) === 200 which should be 800
You can fix this using a slightly more complex function. You can add it to the Math object or use a stand alone function as follows.
const modAbs = (value, modulo) => (value % modulo + modulo) % modulo;
With the above function negative values are correctly moved into positive space.
So if you have a coordinate x, y to make it warp the screen with
x = modAbs(x, screenWidth);
y = modAbs(y, screenHeight);
That seams easy, but unfortunately there are still some problems to overcome.
Popping
Using the above function to warp across the screen does not consider the size of the sprite, and because you are rendering only one copy when the sprite is move across the playfield edge it will not appear at the other side until the coordinate crossed the edge.
This causes the sprite to pop in and or out depending on the direction of movement and the position of the sprites origin.
There are two solutions.
Extend the playfield
If you make the playfield larger than the view (Viewable area) by 2 times the size of the sprite and warp using the larger playfield then the sprite will not warp until it has completely disappeared from view. This prevents the ugly popping in and out when warping and is most suited to NPC type sprites. For player (focused) sprites this is not a good options as the sprite will not be completely visible as it crosses the screen edges.
Render extra copies.
To keep the sprite fully visible at all times you need to render it more than once when it is crossing the screen. Example pseudo code
// use modulo to warp
x = modAbs(x, screenWidth);
// check if sprite overlaps the screen edge
if (x + spriteWidth > screenWidth) { // is crossing then
drawSprite(x - screenWidth, // ... draw a copy at opposite edge.
If you are only warping between top and bottom (or left and right) this is all that is needed.
If you are warping in all directions you will need to render the sprite up to 4 times. Twice when crossing top bottom or left right. 4 times if crossing in a corner.
As your question only indicates up and down warps I assume you don't need the extra code.

How do you avoid the "RangeError: Maximum call stack size exceeded" error?

I'm currently working on a maze generating algorithm called recursive division. The algorithm is quite simple to understand: Step 1: if the height of your chamber is smaller than the width, divide your grid/chamber with a vertical line. If the height is greater than the width, then divide your chamber with a horizontal line. Step 2: Repeat step 1 with the sub-chambers that were created by the lines. You want to repeat these steps until you get a maze (until the width or height equals 1 unit).
The problem that I have with this algorithm is that JavaScript prints out a RangeError, meaning that I called the function that creates the maze too many times (I'm trying to implement this algorithm with a recursive function). Is there any way to avoid/prevent this from happening? Or am I missing something important in my code that makes the algorithm not work properly?
I have tried to implement a trampoline function, but since I'm a beginner I just don't understand it well enough to implement my self. I have also restarted my entire project ruffly 3 times with some hope that I will come up with a different approach to this problem, but I get the same error every time.
My code here:
//leftCord = the left most x coordinate of my chamber/grid, upCord = the upmost y coordinate of my
grid etc.
//(0, 0) IS POSITIONED IN THE LEFT TOP NODE OF MY GRID
function createMaze(leftCord, rightCord, upCord, downCord) {
var height = Math.abs(downCord - upCord);
var width = Math.abs(rightCord - leftCord);
if (height < 2 || width < 2) {
//The maze is completed!
return;
} else {
if (height < width) {
//cut the chamber/grid vertically
//Getting a random number that's EVEN and drawing the function x = 'random number' on the grid
var x = randomNum(leftCord / 2, rightCord / 2) * 2;
var lineX = [];
for (i = upCord; i < downCord; i++) {
lineX.push(grid[i][x]);
}
//Making a random door/passage and making sure it's ODD
var randomDoor = randomNum(0, lineX.length / 2) * 2 + 1;
lineX.splice(randomDoor, 1);
//Drawing the line
for (i = 0; i < lineX.length; i++) {
lineX[i].className = "wall";
}
//Making the same thing again, but with the left and right sub-chambers that were created by the line
createMaze(leftCord, x, upCord, downCord);
createMaze(x, rightCord, upCord, downCord);
} else {
//cut the chamber/grid horizontally
//Getting a random number that's EVEN and drawing the function y = 'random number' on the grid
var y = randomNum(0, downCord / 2) * 2;
var lineY = [];
for (i = leftCord; i < rightCord; i++) {
lineY.push(grid[y][i]);
}
//Making a random door/passage and making sure it's ODD
var randomDoor = randomNum(0, lineY.length / 2) * 2 + 1;
lineY.splice(randomDoor, 1);
//Drawing the line
for(i = 0; i < lineY.length; i++){
lineY[i].className = "wall";
}
//Making the same thing again, but with the upper and lower-chambers that were created by the line
createMaze(leftCord, rightCord, upCord, y);
createMaze(leftCord, rightCord, y, downCord);
}
}
}
This is happening because you never initialize i with var- it is sent into the global scope and is overwritten each function call.

Is ist possible to get/set the radius of an existing path.circle?

Starting with a path - or many of these ...
var c=paper.Path.Circle(centerPoint, 30);
c.strokeColor="";
I want to have that circle grow its radius linearly. I can do this:
var children = paper.project.activeLayer.children;
paper.view.onFrame = function(event) {
for (var i = 0; i < children.length; i++) {
var item = children[i];
item.scale(1.01);
}
But that increases the radius exponentially !
Can I get the radius of a circle and change it ? Or do I have to create a new one, deleting the old one ?
How does scale() do this ?
I also would like to delete circle larger that a given size.
Thanks,
Sebastian
You can get the radius of a circle though it's indirect.
var radius = circle.bounds.topCenter.y - circle.bounds.center.y;
or
var radius = circle.bounds.width / 2
give you the radius of a circle. But a circle is stored as 4 segments with handles in and out, not as a circle-type object, so the radius is not stored anywhere.
In order to make it seem to grow you will have to delete the old one and draw a new one of the larger size.
It's also possible to scale it, but you want to scale it without compounding the scaling. So if you want it to grow 1.01 then 1.02 instead of 1.0201, etc. you will need to adjust the scaling factor each time.
It's not perfectly clear how you want to grow the circle, but here's some code that makes some assumptions about what you want to do:
function Scale() {
this.original = 1.0;
this.current = 1.0;
}
// target refers to original size in fractional terms, e.g., to
// grow by 1% specify 1.01 or to shrink by 1% specify 0.99. It returns
// the scale factor to apply to the current scale to achieve the
// target. So to increase the scale by 10% of the original size each
// time:
//
// var s = new Scale();
//
// for (i = 1.1; i <= 2.05; i += 0.1) {
// var scaleFactor = s.scale(i);
// }
//
// note the i <= 2.05 to allow for real number math issues.
//
Scale.prototype.scale = function(target) {
// get the scaling factor from the original size
var oFactor = target / this.original;
// now get the factor to scale the current size by
var cFactor = oFactor / this.current;
this.current = oFactor;
return cFactor;
}

jQuery Circles animation on circle path

I'm trying to make big circle and move divs along the circle's circumference.
Each div must change the content inside the big circle.
The number of div(s) must be dependent on how many are fetched from database (from table category).
I tried to do this and modified the code by putting .eq() but the problem with .eq is that next circle will appear after that circle, all put in the same place. I want them all to appear at the same time like this without repeating functions
Updated your fiddle:
http://jsfiddle.net/wyW2D/1/
Used:
var t = -1.6;
var t2 = -1.6;
var x = 0;
var t = [-1.6, -1.6, -1.6], // starting angle in radians for each circle
delta = [0.05, 0.03, 0.02], // change in radians for each circle
// e.g the first moves fastest, the last
// slowest. if this gets too big, the
// movement won't look circular, since the
// animation is really a bunch of straight lines
finish = [1.4, 1.0, 0.6]; // the stopping point in radians for each
// circle. if the circle size changes, this
// may need to change
function moveit(i) {
t[i] += delta[i]; // move the angle forward by delta
var r = 300; // radius (the .inner div is 600 x 600)
var xcenter = -30; // center X position: this reproduces the .inner horizontal
// center but from the body element
var ycenter = 420; // center Y position: same here but vertical
// Basic trig, these use sin/cos to find the vert and horiz offset for a given
// angle from the center (t[i]) and a given radius (r)
var newLeft = Math.floor(xcenter + (r * Math.cos(t[i])));
var newTop = Math.floor(ycenter + (r * Math.sin(t[i])));
// Now animate to the new top and left, over 1ms, and when complete call
// the move function again if t[i] hasn't reached the finish.
$('div.circle'+(i+1)).animate({
top: newTop,
left: newLeft,
},1, function() {
if (t[i] < finish[i]) moveit(i);
});
// You can slow down the animation by increasing the 1, but that will eventually
// make it choppy. This plays opposite the delta.
}
// Start the ball rolling
$("document").ready(function(e) {
moveit(0);
moveit(1);
moveit(2);
});
This was a quick change to reduce the code to one function that used arrays (t, delta, finish) to keep track of the three circles. It could be improved to accept arbitrary circles, of any size, at any starting / ending angle.
Also, this kind of animation is much easier with CSS. It is simple to specify and has much better performance.

randomly mapping divs

I am creating a new "whack-a-mole" style game where the children have to hit the correct numbers in accordance to the question. So far it is going really well, I have a timer, count the right and wrong answers and when the game is started I have a number of divs called "characters" that appear in the container randomly at set times.
The problem I am having is that because it is completely random, sometimes the "characters" appear overlapped with one another. Is there a way to organize them so that they appear in set places in the container and don't overlap when they appear.
Here I have the code that maps the divs to the container..
function randomFromTo(from, to) {
return Math.floor(Math.random() * (to - from + 1) + from);
}
function scramble() {
var children = $('#container').children();
var randomId = randomFromTo(1, children.length);
moveRandom('char' + randomId);
}
function moveRandom(id) {
var cPos = $('#container').offset();
var cHeight = $('#container').height();
var cWidth = $('#container').width();
var pad = parseInt($('#container').css('padding-top').replace('px', ''));
var bHeight = $('#' + id).height();
var bWidth = $('#' + id).width();
maxY = cPos.top + cHeight - bHeight - pad;
maxX = cPos.left + cWidth - bWidth - pad;
minY = cPos.top + pad;
minX = cPos.left + pad;
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX);
$('#' + id).css({
top: newY,
left: newX
}).fadeIn(100, function () {
setTimeout(function () {
$('#' + id).fadeOut(100);
window.cont++;
}, 1000);
});
I have a fiddle if it helps.. http://jsfiddle.net/pUwKb/8/
As #aug suggests, you should know where you cannot place things at draw-time, and only place them at valid positions. The easiest way to do this is to keep currently-occupied positions handy to check them against proposed locations.
I suggest something like
// locations of current divs; elements like {x: 10, y: 40}
var boxes = [];
// p point; b box top-left corner; w and h width and height
function inside(p, w, h, b) {
return (p.x >= b.x) && (p.y >= b.y) && (p.x < b.x + w) && (p.y < b.y + h);
}
// a and b box top-left corners; w and h width and height; m is margin
function overlaps(a, b, w, h, m) {
var corners = [a, {x:a.x+w, y:a.y}, {x:a.x, y:a.y+h}, {x:a.x+w, y:a.y+h}];
var bWithMargins = {x:b.x-m, y:b.y-m};
for (var i=0; i<corners.length; i++) {
if (inside(corners[i], bWithMargins, w+2*m, h+2*m) return true;
}
return false;
}
// when placing a new piece
var box;
while (box === undefined) {
box = createRandomPosition(); // returns something like {x: 15, y: 92}
for (var i=0; i<boxes.length; i++) {
if (overlaps(box, boxes[i], boxwidth, boxheight, margin)) {
box = undefined;
break;
}
}
}
boxes.push(box);
Warning: untested code, beware the typos.
The basic idea you will have to implement is that when a random coordinate is chosen, theoretically you SHOULD know the boundaries of what is not permissible and your program should know not to choose those places (whether you find an algorithm or way of simply disregarding those ranges or your program constantly checks to make sure that the number chosen isn't within the boundary is up to you. the latter is easier to implement but is a bad way of going about it simply because you are entirely relying on chance).
Let's say for example coordinate 50, 70 is selected. If the picture is 50x50 in size, the range of what is allowed would exclude not only the dimensions of the picture, but also 50px in all directions of the picture so that no overlap may occur.
Hope this helps. If I have time, I might try to code an example but I hope this answers the conceptual aspect of the question if that is what you were having trouble with.
Oh and btw forgot to say really great job on this program. It looks awesome :)
You can approach this problem in at least two ways (these two are popped up in my head).
How about to create a 2 dimensional grid segmentation based on the number of questions, the sizes of the question panel and an array holding the position of each question coordinates and then on each time frame to position randomly these panels on one of the allowed coordinates.
Note: read this article for further information: http://eloquentjavascript.net/chapter8.html
The second approach follow the same principle, but this time to check if the panel overlap the existing panel before you place it on the canvas.
var _grids;
var GRID_SIZE = 20 //a constant holding the panel size;
function createGrids() {
_grids = new Array();
for (var i = 0; i< stage.stageWidth / GRID_SIZE; i++) {
_grids[i] = new Array();
for (var j = 0; j< stage.stageHeight / GRID_SIZE; j++) {
_grids[i][j] = new Array();
}
}
}
Then on a separate function to create the collision check. I've created a gist for collision check in Actionscript, but you can use the same principle in Javascript too. I've created this gist for inspirational purposes.
Just use a random number which is based on the width of your board and then modulo with the height...
You get a cell which is where you can put the mole.
For the positions the x and y should never change as you have 9 spots lets say where the mole could pop up.
x x x
x x x
x x x
Each cell would be sized based on % rather then pixels and would allow re sizing the screen
1%3 = 1 (x)
3%3 = 0 (y)
Then no overlap is possible.
Once the mole is positioned it can be show or hidden or moved etc based on some extended logic if required.
If want to keep things your way and you just need a quick re-position algorithm... just set the NE to the SW if the X + width >= x of the character you want to check by setting the x = y+height of the item which overlaps. You could also enforce that logic in the drawing routine by caching the last x and ensuring the random number was not < last + width of the item.
newY = randomFromTo(minY, maxY);
newX = randomFromTo(minX, maxX); if(newX > lastX + characterWidth){ /*needful*/}
There could still however be overlap...
If you wanted to totally eliminate it you would need to keep track of state such as where each x was and then iterate that list to find a new position or position them first and then all them to move about randomly without intersecting which would would be able to control with just padding from that point.
Overall I think it would be easier to just keep X starting at 0 and then and then increment until you are at a X + character width > greater then the width of the board. Then just increase Y by character height and Set X = 0 or character width or some other offset.
newX = 0; newX += characterWidth; if(newX + chracterWidth > boardWidth) newX=0; newY+= characterHeight;
That results in no overlap and having nothing to iterate or keep track of additional to what you do now, the only downside is the pattern of the displayed characters being 'checker board style' or right next to each other (with possible random spacing in between horizontal and vertical placement e.g. you could adjust the padding randomly if you wanted too)
It's the whole random thing in the first place that adds the complexity.
AND I updated your fiddle to prove I eliminated the random and stopped the overlap :)
http://jsfiddle.net/pUwKb/51/

Categories