I'm trying to write a function that returns the position of a tile on a tileset.
I wrote some code for that but that seems unnecessarily complicated.
how each tile has a number:
// My old code:
function getXPosByNumber(width, height, tilesize, tilenumber) {
if ((tilenumber % (width / tilesize) - 1) < 0) {
return width - tilesize;
} else {
return (tilenumber % (width / tilesize) - 1) * tilesize;
}
}
function getYPosByNumber(width, height, tilesize, tilenumber) {
return (Math.ceil(tilenumber / (width / tilesize)) - 1) * tilesize;
}
How can I access any tile coordonates with a cleaner/simpler code ?
Thanks.
Here's what I figured out to simplify. I found that height isn't so relevant to calculate either X or Y. The important factor here is how much it spreads horizontally.
Then, since a specific tileset will more likely always have the same width and tilesize, I suggest a more "Object"-ified approach so you don't have to constantly remember its configuration. Personally, I like to wrap constants and configurations so I can write lightweight code afterwards:
var tileset = function(width, tilesize) {
this.width = width;
this.tilesize = tilesize;
};
// calculate X by tilenumber
tileset.prototype.getX = function(tilenumber) {
return (tilenumber % this.width - 1) * this.tilesize;
};
// calculate Y by tilenumber
tileset.prototype.getY = function(tilenumber) {
return Math.floor(tilenumber / this.width) * this.tilesize;
};
That allows you to use it like this:
var mySet = new tileset(6, 10); // width=6, tilesize=10
var x = mySet.getX(16); // tilenumber=16
var y = mySet.getY(16); // tilenumber=16
To see it in action, check this FIDDLE.
Please note that it calculates the position of top left corner. It can be adjusted otherwise if needed.
Hope this helps!
If this tileset is always the same I would suggest to create a kind of lookup table. An object that will store precalculated positions for each tile number. It will be much simpler and faster since you only need to access property of an object.
Related
In this example we use math functions to create a blurred copy of an image. The way we will blur the image is by scrambling pixels that are near each other.
We begin by creating a blank image and writing the loop to let us color each pixel in the image. For each pixel we will do one of two things: half the time, we will simply copy the pixel from the old picture into the new picture without changing anything. The other half of the time we will find a pixel nearby and copy that one instead.
Now we must figure out how to find a "nearby" pixel. We will define some value for how far away the new pixel will be (we used 10 pixels) and then we write a function that will give a (x,y) point that is a random amount between 0 and 10 pixels away in each direction. Before we use the new (x,y) point, we must check to be sure it is still a valid pixel position in the image. For example, we may be at a pixel that is on the very top of the image. Our random point generator tells us to go up by 3 pixels, but since we are on the top of the image (y = 0) we cannot very well go up by three pixels (y would be -3)! If the random number is too big (larger than the dimension -1) or too small (less than 0) then we will just use the closest number that is valid.
Once we have a valid pixel that is some amount away we use its red, green, and blue values as the new pixel's values.
function nearby(image, x, y) {
var newX = x + Math.random() * 10 - 5;
if (newX > image1.getWidth()) {
newX = newX - 5;
}
if (newX < 0) {
newX = newX + 5;
} else {
newX = newX;
}
var newY = y + Math.random() * 10 - 5;
if (newY > image1.getHeight()) {
newY = newY - 5;
}
if (newY < 0) {
newY = newY + 5;
} else {
newY = newY;
}
return image.getPixel(newX, newY);
}
function blur(image) {
for (var pixel of image.values()) {
x = pixel.getX();
y = pixel.getY();
var orgPixel = image.getPixel(x, y);
if (Math.random() < 0.5) {
var other = nearby(image, x, y);
output.setPixel(x, y, other);
} else {
output.setPixel(x, y, orgPixel);
}
}
return output;
}
var image1 = new SimpleImage("duvall.jpg");
var output = new SimpleImage(image1.getWidth(), image1.getHeight());
blur(image1);
print(output);
----i used one of the pictures in my file but any picture should do.
I'm stumped on what is probably some pretty simple math. I need to get the X and Y coordinates from each tiles referenced ID. The grid below shows the order the ids are generated in. Each tile has a width and height of 32. Number ones x & y would be equal to (0,0). This is for a game I'm starting to make with canvas using a tileset.
1|2|3
4|5|6
7|8|9
So far for X, I've come up with...
(n % 3) * 32 - 32 // 3 is the width of the source image divded by 32
And for Y...
(n / 3) * 32
This is obviously wrong, but It's the closest I've come, and I don't think I'm too far off from the actual formula.
Here is my actual code so far:
function startGame() {
const canvas = document.getElementById("rpg");
const ctx = canvas.getContext("2d");
const tileSet = new Image();
tileSet.src = "dungeon_tiles.png";
let map = {
cols: 10,
rows: 10,
tsize: 32,
getTileX: function(counter, tiles) {
return ((tiles[counter] - 1) % 64) * 32;
},
getTileY: function(counter, tiles) {
return ((tiles[counter] - 1) / 64) * 32;
}
};
let counter = 0;
tileSet.onload = function() {
for (let c = 0; c < map.cols; c++) {
for (let r = 0; r < map.rows; r++) {
let x = map.getTileX(counter, mapObj.layers[0].data); // mapObj.layers[0].data is the array of values
let y = map.getTileY(counter, mapObj.layers[0].data);
counter += 1;
ctx.drawImage(
tileSet, // image
x, // source x
y, // source y
map.tsize, // source width
map.tsize, // source height
r * map.tsize, // target x
c * map.tsize, // target y
map.tsize, // target width
map.tsize // target height
);
}
}
};
}
If 1 is (0,0) and each tile is 32*32, then finding your horizontal position is a simple 32*(t-1) where t is your tile number. t-1 because your tiles start from 1 instead of 0. Now, you have 3 tiles per row so you want to reset every 3, so the final formula for your x is 32*((t-1)%3).
For the vertical position it's almost the same, but you want to increase your position by 32 only once every 3 tiles, so this is your y: 32*floor((t-1)/3).
floor((t-1)/3) is simply integer division since the numbers are always positive.
If I understand this correctly, you want to get the 1|2|3 values based on x, y correct? You can do something like this:
((y * total # of rows) + x) + 1
This would convert the 2D x, y index to a single index which is, as you stated, 1|2|3. This formula is based on your example where count starts at 1 and not 0. If you want to convert it to 0 base, just remove the + 1.
If you have the width and height, or probably location of input/character, you can have a GetX(int posX) and GetY(int posY) to get the x and y based on the position. Once you have converted the position to x, y values, use the formula above.
int GetX(int posX)
{
return (posX / 32);
}
int GetY(int posY)
{
return (posY / 32);
}
int GetIndex(int posX, int posY)
{
return ((GetY(posY) / totalRows) + GetX(posX)) + 1;
}
So I am creating a flappy bird game (If you understand what this game is it would make it much easier for you to understand what I am about to say) and inside this game I have 2 pipes. One of the pipes is located at the lowest y value(600px) and one starts at a y value of 0. So these pipes essentially have opposite y values but the same X values(the X value also moves, but they are still the same value of X). The height of the pipes are also randomly generated. Question starts here: what I want for this code is after a number of tubeX pixels traveled ( a time interval could also work), I want to add another set of pipes and do the same with that value. But the values of the old pipes must stay the same. I think an array of some sort would be best but my javascript coding abilities are quite low and would have not a clue how to implement something like that into my code until I see it.
This bit of code generates random heights:
function pipeY() {
var top = Math.random() * -32 + 1;
height1 = top * 10;
var bottom = Math.random() * 32 + 1;
height2 = bottom *10
loop();
}
this creates a moving X value for the pipes:
tubeX = 400;
velocityX = 0;
force = -0.5;
function pipeX() {
velocityX += force;
velocityX *= 0.9;
tubeX += velocityX;
}
This creates the two pipes that are opposite of each other:
function show() {
var tubeHeight1 = 600;
var tubeHeight2 = 0;
ctx.fillStyle="green";
tube1 = ctx.fillRect(tubeX,tubeHeight1,5,height1);
tube2 = ctx.fillRect(tubeX,tubeHeight2,5,height2);
ctx.stroke();
}
What I ended up doing was creating a while loop inside the pipeX() function. I also changed the values of tubeX to 800 so it just creates a new pipe more efficiently at 400 px. This probably is not the most efficient way but it looks good!
function pipeX() {
var lastTime = 0;
velocityX += force;
velocityX *= 0.9;
tubeX += velocityX;
while (tubeX < -25) {
show();
if (tubeX = 400) {
new pipeY();
new show();
}
}
}
I was working on a fun project that implicates creating "imperfect" circles by drawing them with lines and animate their points to generate a pleasing effect.
The points should alternate between moving away and closer to the center of the circle, to illustrate:
I think I was able to accomplish that, the problem is when I try to render it in a canvas half the render jitters like crazy, you can see it in this demo.
You can see how it renders for me in this video. If you pay close attention the bottom right half of the render runs smoothly while the top left just..doesn't.
This is how I create the points:
for (var i = 0; i < q; i++) {
var a = toRad(aDiv * i);
var e = rand(this.e, 1);
var x = Math.cos(a) * (this.r * e) + this.x;
var y = Math.sin(a) * (this.r * e) + this.y;
this.points.push({
x: x,
y: y,
initX: x,
initY: y,
reverseX: false,
reverseY: false,
finalX: x + 5 * Math.cos(a),
finalY: y + 5 * Math.sin(a)
});
}
Each point in the imperfect circle is calculated using an angle and a random distance that it's not particularly relevant (it relies on a few parameters).
I think it's starts to mess up when I assign the final values (finalX,finalY), the animation is supposed to alternate between those and their initial values, but only half of the render accomplishes it.
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I can't figure it out, thanks in advance!
Is the math wrong? Is the code wrong? Or is it just that my computer can't handle the rendering?
I Think that your animation function has not care about the elapsed time. Simply the animation occurs very fast. The number of requestAnimationFrame callbacks is usually 60 times per second, So Happens just what is expected to happen.
I made some fixes in this fiddle. This animate function take care about timestamp. Also I made a gradient in the animation to alternate between their final and initial positions smoothly.
ImperfectCircle.prototype.animate = function (timestamp) {
var factor = 4;
var stepTime = 400;
for (var i = 0, l = this.points.length; i < l; i++) {
var point = this.points[i];
var direction = Math.floor(timestamp/stepTime)%2;
var stepProgress = timestamp % stepTime * 100 / stepTime;
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
}
}
Step by Step:
based on comments
// 1. Calculates the steps as int: Math.floor(timestamp/stepTime)
// 2. Modulo to know if even step or odd step: %2
var direction = Math.floor(timestamp/stepTime)%2;
// 1. Calculates the step progress: timestamp % stepTime
// 2. Convert it to a percentage: * 100 / stepTime
var stepProgress = timestamp % stepTime * 100 / stepTime;
// if odd invert the percentage.
stepProgress = (direction == 0 ? stepProgress: 100 -stepProgress);
// recompute position based on step percentage
// factor is for fine adjustment.
point.x = point.initX + (Math.cos(point.angle) * stepProgress/100 * factor);
point.y = point.initY + (Math.sin(point.angle) * stepProgress/100 * factor);
EDIT: So apparently, PI is finite in JavaScript (which makes sense). But that leaves me with a major problem. What's the next best way to calculate the angles I need?
Alright, first, my code:
http://jsfiddle.net/joshlalonde/vtfyj/34/
I'm drawing cubes that open up to a 120 degree angle.
So the coordinates are calculated based on (h)eight and theta (120).
On line 46, I have a for loop that contains a nested for loop used for creating rows/columns.
It's somewhat subtle, but I noticed that the lines aren't matching up exactly. The code for figuring out each cubes position is on line 49. One of the things in the first parameter (my x value) for the origin of the cube is off. Can anyone help figure out what it is?
var cube = new Cube(
origin.x + (j * -w * (Math.PI)) +
(i * w * (Math.PI))
, origin.y + j * (h / 2) +
i * (h / 2) +
(-k*h), h);
Sorry if that's confusing. I,j, and k refer to the variable being incremented by the for loops. So basically, a three dimensional for loop.
I think the problem lies with Math.PI.
The width isn't the problem, or so I believe. I originally used 3.2 (which I somehow guessed and it seemed to line up pretty good. But I have no clue what the magical number is). I'm guessing it has to do with the angle being converted to Radians, but I don't understand why Math.PI/180 isn't the solution. I tried multiple things. 60 (in degrees) * Math.PI/180 doesn't work. What is it for?
EDIT: It might just be a JavaScript related math problem. The math is theoretically correct but can't be calculated correctly. I'll accept the imperfection to spare myself from re-writing code in unorthodox manners. I can tell it would take a lot to circumvent using trig math.
There are 2 problems...
Change line 35 to var w=h*Math.sin(30);. The 30 here matches the this.theta / 4 in the Cube getWidthmethod since this.theta equals 120.
Use the following code to generate the position of your new cube. You don't need Math.Pi. You needed to use both the cube width and height in your calculation.
var cube = new Cube(
origin.x+ -j*w - i*h,
origin.y + -j*w/2 + i*h/2,
h);
Alright I found the solution!
It's really simple - I was using degrees instead of radians.
function Cube(x, y, h) {
this.x = x
this.y = y
this.h = h;
this.theta = 120*Math.PI/180;
this.getWidth = function () {
return (this.h * Math.sin(this.theta / 2));
};
this.width = this.getWidth();
this.getCorner = function () {
return (this.h / 2);
};
this.corner = this.getCorner();
}
So apparently Javascript trig functions use Radians, so that's one problem.
Next fix I made was to the offset of each point in the cube. It doesn't need one! (o.O idk why. But whatever it works. I left the old code just in case I discover why later on).
function draw() {
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, canvas.width, canvas.height); // Draw a black canvas
var h = 32;
var width = Math.sin(60*Math.PI/180);
var w = h*width;
var row = 9; // column and row will always be same (to make cube)
var column = row;
var area = row * column;
var height = 1;
row--;
column--;
height--;
var origin = {
x: canvas.width / 2,
y: (canvas.height / 2) - (h * column/2) + height*h
};
var offset = Math.sqrt(3)/2;
offset = 1;
for (var i = 0; i <= row; i++) {
for (var j = 0; j <= column; j++) {
for (var k = 0; k <= height; k++) {
var cube = new Cube(
origin.x + (j * -w * offset) +
(i * w * offset)
, origin.y + (j * (h / 2) * offset) +
(i * (h / 2) * offset) +
(-k*h*offset), h);
var cubes = {};
cubes[i+j+k] = cube; // Store to array
if (j == column) {
drawCube(2, cube);
}
if (i == row) {
drawCube(1, cube);
}
if (k == height) {
drawCube(0,cube);
}
}
}
}
}
See the full Jsfiddle here: http://jsfiddle.net/joshlalonde/vtfyj/41/