I have a problem I just cannot solve, and after a week it's really winding me up.
Background.
I'm placing items onto a circle using basic trig. The number of items can change dynamically, and they are spaced around the circle equally.
The items rotate around the circle, and the speed of rotation changes to be in sync with a BPM (Beats Per Minute) clock. This clock can change at any time.
The problem I'm having is that the items seem to be placed randomly on the circle, not equally spaced in order (see image 1). They'll appear out of order even though it's a basic for loop that places them. I think they may in face be in order, but the rotation values may be off making them look like they are in an odd order.
The second issue is that when the number of items reduces, the speed of rotation increases (it shouldn't) and if the number increases, the speed slows.
So I expect an issue with my trig function. I'm showing the complete code here but can simplify if it'll help.
What have I tried?
I've tried simplified versions without the graphical output, and the numbers all seem to make perfect sense. The radians between items is correct, the placement looks correct. It all looks correct, but it isn't.
The code.
--the variables
orbitalCircle.xPos = x or 0
orbitalCircle.yPos = y or 0
orbitalCircle.circleDiameter = diameter or 10
orbitalCircle.numberOfNotes = number_of_notes
orbitalCircle.spaceBetweenNotes = (360 / number_of_notes)
orbitalCircle.beatsPerSecond = (beats_per_minute / 60)
orbitalCircle.currentRotation = 0
orbitalCircle.framesPerSecond = frames_per_second or 15
orbitalCircle.framesPerFullRotation = (orbitalCircle.numberOfNotes/orbitalCircle.beatsPerSecond)+orbitalCircle.framesPerSecond
orbitalCircle.degreesPerFrame = 360 / orbitalCircle.framesPerFullRotation
orbitalCircle.newRotationValue = orbitalCircle.currentRotation + orbitalCircle.degreesPerFrame
orbitalCircle.sequenceData = sequence_data
--the function that updates the sequence data and therefore the number of items on the circle
function orbitalCircle.updateNotes(sq)
orbitalCircle.sequenceData = sq
orbitalCircle.numberOfNotes = (#sq)
orbitalCircle.spaceBetweenNotes = (360 / orbitalCircle.numberOfNotes)
end
--the function that calculates the new rotation value of the item to be placed on the circle
function orbitalCircle.tick()
orbitalCircle.spaceBetweenNotes = (360 / number_of_notes)
orbitalCircle.framesPerFullRotation = (orbitalCircle.numberOfNotes/orbitalCircle.beatsPerSecond)*orbitalCircle.framesPerSecond
orbitalCircle.degreesPerFrame = (360 / orbitalCircle.framesPerFullRotation)
orbitalCircle.newRotationValue = (orbitalCircle.currentRotation + orbitalCircle.degreesPerFrame)
if orbitalCircle.newRotationValue > 360 then
orbitalCircle.currentRotation = 0
else
orbitalCircle.currentRotation = orbitalCircle.newRotationValue
end
end
--finally the function that places the items onto the circle
function orbitalCircle.redraw()
screen.circle(orbitalCircle.xPos, orbitalCircle.yPos, orbitalCircle.circleDiameter)
screen.stroke()
for i=1, (#orbitalCircle.sequenceData) do
if orbitalCircle.sequenceData[i] > 0 then
screen.circle(
math.cos(math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i))*orbitalCircle.circleDiameter + orbitalCircle.xPos,
math.sin(math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i))*orbitalCircle.circleDiameter + orbitalCircle.yPos,
map(orbitalCircle.sequenceData[i], 5, 128, 0.5, 4)
)
end
end
end
end
I'd expect that the items would be:
equally spaced no matter the amount (that works)
in order (they appear not to be)
the speed of rotation should remain fixed unless the BPM changes (this doesn't happen)
I'm lost!
Let us take a closer look at the drawing.
screen.circle(
math.cos(math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i))*orbitalCircle.circleDiameter + orbitalCircle.xPos,
math.sin(math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i))*orbitalCircle.circleDiameter + orbitalCircle.yPos,
map(orbitalCircle.sequenceData[i], 5, 128, 0.5, 4)
)
What is the angle that is being drawn here? It is the argument to math.cos and math.sin (I will ignore the scaling and the translation that is applied afterwards):
math.rad(orbitalCircle.newRotationValue)+(orbitalCircle.spaceBetweenNotes*i)
So... it is the neRotationValue converted to radians and added to that the space between notes. This one is defined as 360 / number_of_notes, so it is in degrees. Adding a radians and degrees most likely does not produce the expected result.
So, what exactly do you mean with the following?
I've tried simplified versions without the graphical output, and the numbers all seem to make perfect sense.
Related
I am making a Breakout Game and the Game generates rows from how ever many bricks I want in that row. But I cannot get the rows to start in the center. How would I achieve this, here is my current code.
I've tried numerous things, but the closest I got was to get them all to be in the middle, but the should spread out from the middle.
What I would like to achieve should look something like this.
If you have the maximum row size available, a simple way is to include an offset when you calculate the x coordinate.
let maxRow = ... // whatever the maximum row size is
let padding = (maxRow - row) / 2 // how many blocks to skip on the left
for i in 1...row {
// your block creation loop
let x = 15 + (i + padding) * Int(brick.size.width)
// make the brick at x, y
}
If things might not divide evenly (e.g., if maxRow is 15 like it seems to be in your pictures, but row could be 4) then you have to decide on what behavior you'd like. The above will keep bricks aligned because the integer division in calculating padding will truncate; the left and right padding won't be the same. If you use floating division and get a padding like 3.5, you'd get perfectly centered rows but odd rows and even rows would have different alignment.
If your problem is that you want things to appear in an animated way (like in your picture), then things are more complicated. Instead of generating based on the sequence 1, 2, ..., row, it's probably easier to think about generating from the sequence 0, +1, -1, +2, -2, ... After generating the center brick, you'd do the generation of additional bricks in pairs. The x coordinates would be calculated as xCenter + i * Int(brick.side.width).
You're adding the bricks into a parent SKNode (using addChild(_:)). Everything added in that parent node is relative to its coordinate space.
Try adding a brick at y: 0 and see where will it be positioned. If your scene has the anchorPoint at 0.5, you will likely see the brick in the center of the screen. From there, you go up or down (positive or negative y values) to show other rows.
To better understand how the coordinate system works, see About SpriteKit Coordinate Systems.
It's the same for the x axis. If your bricks start from left to right, that means the parent node has its origin to the left of the screen.
To start from the middle, you need to know the middle point. One option is to make a container node, position that in the center of the screen, and add all the bricks there. Like that, the bricks can start at (0, 0).
let middle = CGPoint(x: scene!.size.width / 2, y: scene!.size.height / 2)
let container = SKNode()
container.position = middle
// inside makeBricks(), add everything inside container:
container.addChild(brick)
It really depends on how you set up your node hierarchy.
I want to detect pixel-perfect collisions between 2 sprites.
I use the following function which I have found online, but makes total sense to me.
static bool PerPixelCollision(Sprite a, Sprite b)
{
// Get Color data of each Texture
Color[] bitsA = new Color[a.Width * a.Height];
a.Texture.GetData(0, a.CurrentFrameRectangle, bitsA, 0, a.Width * a.Height);
Color[] bitsB = new Color[b.Width * b.Height];
b.Texture.GetData(0, b.CurrentFrameRectangle, bitsB, 0, b.Width * b.Height);
// Calculate the intersecting rectangle
int x1 = (int)Math.Floor(Math.Max(a.Bounds.X, b.Bounds.X));
int x2 = (int)Math.Floor(Math.Min(a.Bounds.X + a.Bounds.Width, b.Bounds.X + b.Bounds.Width));
int y1 = (int)Math.Floor(Math.Max(a.Bounds.Y, b.Bounds.Y));
int y2 = (int)Math.Floor(Math.Min(a.Bounds.Y + a.Bounds.Height, b.Bounds.Y + b.Bounds.Height));
// For each single pixel in the intersecting rectangle
for (int y = y1; y < y2; ++y)
{
for (int x = x1; x < x2; ++x)
{
// Get the color from each texture
Color colorA = bitsA[(x - (int)Math.Floor(a.Bounds.X)) + (y - (int)Math.Floor(a.Bounds.Y)) * a.Texture.Width];
Color colorB = bitsB[(x - (int)Math.Floor(b.Bounds.X)) + (y - (int)Math.Floor(b.Bounds.Y)) * b.Texture.Width];
if (colorA.A != 0 && colorB.A != 0) // If both colors are not transparent (the alpha channel is not 0), then there is a collision
{
return true;
}
}
}
//If no collision occurred by now, we're clear.
return false;
}
(all the Math.floor are useless, I copied this function from my current code where I'm trying to make it work with floats).
It reads the color of the sprites in the rectangle portion that is common to both sprites.
This actually works fine, when I display the sprites at x/y coordinates where x and y are int's (.Bounds.X and .Bounds.Y):
View an example
The problem with displaying sprites at int's coordinates is that it results in a very jaggy movement in diagonals:
View an example
So ultimately I would like to not cast the sprite position to int's when drawing them, which results in a smooth(er) movement:
View an example
The issue is that the PerPixelCollision works with ints, not floats, so that's why I added all those Math.Floor. As is, it works in most cases, but it's missing one line and one row of checking on the bottom and right (I think) of the common Rectangle because of the rounding induced by Math.Floor:
View an example
When I think about it, I think it makes sense. If x1 is 80 and x2 would actually be 81.5 but is 81 because of the cast, then the loop will only work for x = 80, and therefore miss the last column (in the example gif, the fixed sprite has a transparent column on the left of the visible pixels).
The issue is that no matter how hard I think about this, or no matter what I try (I have tried a lot of things) - I cannot make this work properly. I am almost convinced that x2 and y2 should have Math.Ceiling instead of Math.Floor, so as to "include" the last pixel that otherwise is left out, but then it always gets me an index out of the bitsA or bitsB arrays.
Would anyone be able to adjust this function so that it works when Bounds.X and Bounds.Y are floats?
PS - could the issue possibly come from BoxingViewportAdapter? I am using this (from MonoExtended) to "upscale" my game which is actually 144p.
Remember, there is no such thing as a fractional pixel. For movement purposes, it completely makes sense to use floats for the values and cast them to integer pixels when drawn. The problem is not in the fractional values, but in the way that they are drawn.
The main reason the collisions are not appearing to work correctly is the scaling. The colors for the new pixels in between the diagonals get their colors by averaging* the surrounding pixels. The effect makes the image appear larger than the original, especially on the diagonals.
*there are several methods that may be used for the scaling, bi-cubic and linear are the most common.
The only direct(pixel perfect) solution is to compare the actual output after scaling. This requires rendering the entire screen twice, and requires the scale factor more computations. (not recommended)
Since you are comparing the non-scaled images your collisions appear to be off.
The other issue is movement speed. If you are moving faster than one pixel per Update(), detecting per pixel collisions is not enough, if the movement is to be restricted by the obstacle. You must resolve the collision.
For enemies or environmental hazards your original code is sufficient and collision resolution is not required. It will give the player a minor advantage.
A simple resolution algorithm(see below for a mathematical solution) is to unwind the movement by half, check for collision. If it is still colliding, unwind the movement by a quarter, otherwise advance it by a quarter and check for collision. Repeat until the movement is less than 1 pixel. This runs log of Speed times.
As for the top wall not colliding perfectly: If the starting Y value is not a multiple of the vertical movement speed, you will not land perfectly on zero. I prefer to resolve this by setting the Y = 0, when Y is negative. It is the same for X, and also when X and Y > screen bounds - origin, for the bottom and right of the screen.
I prefer to use mathematical solutions for collision resolution. In your example images, you show a box colliding with a diamond, the diamond shape is represented mathematically as the Manhattan distance(Math.Abs(x1-x2) + Math.Abs(y1-y2)). From this fact, it is easy directly calculate the resolution to the collision.
On optimizations:
Be sure to check that the bounding Rectangles are overlapping before calling this method.
As you have stated, remove all Math.Floors, since, the cast is sufficient. Reduce all calculations inside of the loops not dependent on the loop variable outside of the loop.
The (int)a.Bounds.Y * a.Texture.Width and (int)b.Bounds.Y * b.Texture.Width are not dependent on the x or y variables and should be calculated and stored before the loops. The subtractions 'y-[above variable]` should be stored in the "y" loop.
I would recommend using a bitboard(1 bit per 8 by 8 square) for collisions. It reduces the broad(8x8) collision checks to O(1). For a resolution of 144x144, the entire search space becomes 18x18.
you can wrap your sprite with a rectangle and use its function called Intersect,which detedct collistions.
Intersect - XNA
I have worked day-in and day-out on a ray-casting engine I'm building for the Ti Nspire CX (with Lua), and am having issues with ray collision.
I have fiddled around with the area which I believe has the problem, because drawing of rays on the screen has no issues:
I have also done ALOT of debugging in this, such as displaying the coordinates which the rays are located when being shot outwards from the player. I say that I believe that the collision part has the problem because when I printed the radius of each ray, they all reached the maximum distance, which I set to be 40. This is the collision and single-ray management code:
function ray()
radius = 1
for n = 1, 40, 1 do -- increase of testdot
x_ray = x + (radius * math.cos(rayf * 3.141592653/180))
y_ray = y - (radius * math.sin(rayf * 3.141592653/180))
--print(math.floor(x_ray,3), math.floor(y_ray,3), rayf, radius)
for i = 1, 4, 1 do --for k,v in pairs(map) do -- testing for collision of testdot and a wall
--print("X ",v[1],"<-->",math.floor(x_ray),"<-->",v[3])
--print("Y ",v[2],"<-->",math.floor(y_ray),"<-->",v[4])'
------------------------------------
if (
math.min(map[i][1],map[i][3]) <= x_ray and x_ray <= math.max(map[i][1],map[i][3])
) and (
math.min(map[i][2],map[i][4]) <= y_ray and y_ray <= math.max(map[i][2],map[i][4])
) then
print("Collision")
--return true
end
------------------------------------
end
radius = n
end
end
I know the second for-loop could be condensed, but I did this in my debugging process to find out why this won't function as it should.
The area around the ------------------------------------ is where the rays don't collide/over-reach/miss... I dont know why this isn't working, anyone have any suggestions?
Just for reference, this collision is based off a python program I was having issues with here, in, of course, the collision part.
Values of variables:
x, y is the position of the player (while raycasting this will remain static)
radius is the current radius of a single ray, and will continue to increment as long as no collision is detected
rayf is the current degree of the ray (is not relative to the player). Is calculated at the start of the program by taking the players degree (which isnt shown in here but is called 'facing'), adding 30, then rotating clockwise until the FOV of 60 degrees has been satisfied.
X-ray, y_ray are the current points of a single ray, and will continue to increment towards the specified rayf value, and will increment in values of 1 to make a radius equal to the n in the last for-loop. (Must note that the degrees are the same in a typical unit circle, and are NOT mirrored to match this mirrored y-axis; i.e. 90 degrees is up, 180 degrees is down.)
This isn't code review site but I'm going to try to first write the code in more understandable manner and then to guess the error in comments to the code.
function ray(x,y,rayf,map)
%Are you sure that your global variables x,y,rayf are not overwritten?
%I believe these are correct if 0 degrees is "right", "90" - top and "180" - left
%is it so?
local x_proj = math.cos(rayf* 3.141592653/180);
local y_proj = -math.sin(rayf* 3.141592653/180);
for radius=1,40 do
local x_ray = x + radius * x_proj
local y_ray = y + radius * y_proj
for i=1,4 do
%I take it the map[i] is a particular rectangle located at a particular side of the room (so map[1] is located at the left edge of the screen, for example)
%is it so?
local wall_left_edge = math.min ( map[i][1],map[i][3] )
local wall_right_edge = math.max ( map[i][1],map[i][3] )
%if I understood correctly, the smaller y is above bigger y
local wall_top_edge = math.min ( map[i][2], map[i][4] )
local wall_bottom_edge = math.max ( map[i][2], map[i][4] )
%it is beyond me why couldnt you just sort the wall coordinates beforehand
%(say, top - 1 , bottom - 2 left - 3, right - 4)
if (wall_left_edge < x) and (x < wall_right_edge)
and (wall_top_edge < y) and (y < wall_bottom_edge) then
%this does not detect collision,
%it detects whether beam steps into the rectangle "map[i]"
print("Collision")
end
end
end
end
So, with taking into account the last comment, the walls you define must be broad and thick enough so that the beam is guaranteed to step into one: (wall_right_edge - wall_left_edge ) > 1 ( 1 is step of the radius loop) and (wall_bottom_edge - wall_top_edge ) > 1. At the corners walls must either overlap or they should share a boundary of length at least 1.
The reason the rays were always going past 40 was because the for-loop wasnt being canceled, which is a great reason to include a return to break out of the function and continue the code (I thought a return was included, however it wasn't functioning correctly, as you see in the:
--return true
Past this, the raycasting worked fine, but not math.floor-ing the new ray coord's also made the ray shoot beyond 40, for some unknown reason.
I want to add force to the grenade according to the touch positions of the user.
--this is the code
physics.addBody(grenade1,"dynamic",{density=1,friction=.9,bounce=0})
grenade1:applyForce(event.x,event.y,grenade1.x,grenade1.y)
Here more the x and y positions are the lower the force is. But the force here is too high that the grenade is up in the sky.
You must calibrate the force applied. Remember that F=ma so if x=250 then F=250, if the mass of the display object (set when added body, based on material density * object area) is 1 then acceleration a = 250, which is very large. So try:
local coef = 0.001
grenade1:applyForce(coef*event.x, coef*event.y, grenade1.x, grenade1.y)
and see what you get. If too small, increase coef until the response is what you are looking for. You may find that linear (i.e., constant coef) doesn't give you the effect you want, for example coef=0.01 might be fine for small y but for large y you might find that coef=0.001 works better. In this case you would have to make coef a function of event.y, for example
local coef = event.y < 100 and 0.001 or 0.01
You could also increase the mass of the display object, instead of using coeff.
Recall also that top-level corner is 0,0: force in top level corner will be 0,0. So if you want force to increase as you go up on the screen, you need to use display.contentHeight - event.x.
How to make a 2d world with fixed size, which would repeat itself when reached any side of the map?
When you reach a side of a map you see the opposite side of the map which merged togeather with this one. The idea is that if you didn't have a minimap you would not even notice the transition of map repeating itself.
I have a few ideas how to make it:
1) Keeping total of 3x3 world like these all the time which are exactly the same and updated the same way, just the players exists in only one of them.
2) Another way would be to seperate the map into smaller peaces and add them to required place when asked.
Either way it can be complicated to complete it. I remember that more thatn 10 years ago i played some game like that with soldiers following each other in a repeating wold shooting other AI soldiers.
Mostly waned to hear your thoughts about the idea and how it could be achieved. I'm coding in XNA(C#).
Another alternative is to generate noise using libnoise libraries. The beauty of this is that you can generate noise over a theoretical infinite amount of space.
Take a look at the following:
http://libnoise.sourceforge.net/tutorials/tutorial3.html#tile
There is also an XNA port of the above at: http://bigblackblock.com/tools/libnoisexna
If you end up using the XNA port, you can do something like this:
Perlin perlin = new Perlin();
perlin.Frequency = 0.5f; //height
perlin.Lacunarity = 2f; //frequency increase between octaves
perlin.OctaveCount = 5; //Number of passes
perlin.Persistence = 0.45f; //
perlin.Quality = QualityMode.High;
perlin.Seed = 8;
//Create our 2d map
Noise2D _map = new Noise2D(CHUNKSIZE_WIDTH, CHUNKSIZE_HEIGHT, perlin);
//Get a section
_map.GeneratePlanar(left, right, top, down);
GeneratePlanar is the function to call to get the sections in each direction that will connect seamlessly with the rest of your world.
If the game is tile based I think what you should do is:
Keep only one array for the game area.
Determine the visible area using modulo arithmetics over the size of the game area mod w and h where these are the width and height of the table.
E.g. if the table is 80x100 (0,0) top left coordinates with a width of 80 and height of 100 and the rect of the viewport is at (70,90) with a width of 40 and height of 20 you index with [70-79][0-29] for the x coordinate and [90-99][0-9] for the y. This can be achieved by calculating the index with the following formula:
idx = (n+i)%80 (or%100) where n is the top coordinate(x or y) for the rect and i is in the range for the width/height of the viewport.
This assumes that one step of movement moves the camera with non fractional coordinates.
So this is your second alternative in a little bit more detailed way. If you only want to repeat the terrain, you should separate the contents of the tile. In this case the contents will most likely be generated on the fly since you don't store them.
Hope this helped.