How can I blend a moving circle's motion in an CPU efficient way which still allows for fairly accurate collision detection? I've tried stretching and rotating and ellipse, but it looks unnatural. I've tried drawing in between objects my sketch below. But the motion is still jagged between the frames, and it's expensive to draw a lot of in between circles with transparency. Is there a better way to do this? For example, bridging the shapes or smoothing motion with a bezier transformation? Maybe holding the last two locations in an array and using the current and last two to draw a bezier curve and drawing a curved shape between the last location and current location with the data from all three points? How do you deal with fast motion where you need to avoid the "..." effect, but also need to use the current location for collision detection and so on?
int xprev;
int yprev;
int xcur;
int ycur;
float size = 10;
float scale;
int rotation = 0;
void setup() {
size(600, 600);
}
void draw () {
background(0);
xcur= int(cos(radians(rotation))*200) + width/2;
ycur = int(sin(radians(rotation))*200) + height/2;
rotation = rotation + 5;
//scale = dist(xcur, ycur, xprev, yprev)/2;
//println(scale);
drawEllipse();
xprev = xcur;
yprev = ycur;
}
void drawEllipse() {
for (int i = 1; i < 10; i++) {
noStroke();
fill(255, i*20);
ellipse(xprev+ (xcur - xprev) * i/10, yprev + (ycur - yprev) * i/10, size, size);
}
fill(255);
ellipse(xcur, ycur, size, size);
}
Related
I have a Java program written in Processing I made that draws a spiral in processing but I am not sure how some of the lines of code work. I wrote them based on a tutorial. I added comments in capital letters to the lines I do not understand. The comments in lowercase are lines that I do understand. If you understand how those lines work, please explain in very simple terms! Thank you so much.
void setup()
{
size(500,500);
frameRate(15);
}
void draw()
{
background(0); //fills background with black
noStroke(); //gets rid of stroke
int circlenumber = 999;// determines how many circles will be drawn
float radius = 5; //radius of each small circle
float area = (radius) * (radius) * PI; //area of each small circle
float total = 0; //total areas of circles already drawn
float offset = frameCount * 0.01; //HOW DOES IT WORK & WHAT DOES IT DO
for (int i = 1; i <= circlenumber; ++i) { // loops through all of the circles making up the pattern
float angle = i*19 + offset; //HOW DOES IT WORK & WHAT DOES IT DO
total += area; // adds up the areas of all the small circles that have already been drawn
float amplitude = sqrt( total / PI ); //amplitude of trigonometric spiral
float x = width/2 + cos(angle) * amplitude;//HOW DOES IT WORK & WHAT DOES IT DO
float hue = i;//determines circle color based on circle number
fill(hue, 44, 255);//fills circle with that color
ellipse(x, 1*i, radius*2, radius*2); //draws circle
}
}
Essentially what this is doing is doing a vertical cosine curve with a changing amplitude. Here is a link to a similar thing to what the program is doing. https://www.desmos.com/calculator/p9lwmvknkh
Here is an explanation of this different parts in order. I'm gonna reference some of the variables from the link I provided:
float offset = frameCount * 0.01
What this is doing is determining how quickly the cosine curve is animating. It is the "a" value from desmos. To have the program run, each ellipse must change its angle in the cosine function just a little bit each frame so that it moves. frameCount is a variable that stores the current amount of frames that the animation/sketch has run for, and it goes up every frame, similar to the a-value being animated.
for (int i = 1; i <= circlenumber; ++i) {
float angle = i*19 + offset;
This here is responsible for determining how far from the top the current ellipse should be, modified by a stretching factor. It's increasing each time so that each ellipse is slightly further along in the cosine curve. This is equivalent to the 5(y+a) from desmos. The y-value is the i as it is the dependent variable. That is the case because for each ellipse we need to determine how far it is from the top and then how far it is from the centre. The offset is the a-value because of the reasons discussed above.
float x = width/2 + cos(angle) * amplitude
This calculates how far the ellipse is from the centre of the screen (x-centre, y value is determined for each ellipse by which ellipse it is). The width/2 is simply moving all of the ellipses around the centre line. If you notice on Desmos, the center line is y-axis. Since in Processing, if something goes off screen (either below 0 or above width), we don't actually see it, the tutorial said to offset it so the whole thing shows. The cos(angle)*amplitude is essentially the whole function on Desmos. cos(angle) is the cosine part, while amplitude is the stuff before that. What this can be treated as is essentially just a scaled version of the dependent variable. On desmos, what I'm doing is sqrt(-y+4) while the tutorial essentially did sqrt(25*i). Every frame, the total (area) is reset to 0. Every time we draw a circle, we increase it by the pi * r^2 (area of circle). That is where the dependent variable (i) comes in. If you notice, they write float amplitude = sqrt( total / PI ); so the pi from the area is cancelled out.
One thing to keep in mind is that the circles aren't actually moving down, it's all an illusion. To demonstrate this, here is some modified code that will draw lines. If you track a circle along the line, you'll notice that it doesn't actually move down.
void setup()
{
size(500,500);
frameRate(15);
}
void draw()
{
background(0); //fills background with black
noStroke(); //gets rid of stroke
int circlenumber = 999;// determines how many circles will be drawn
float radius = 5; //radius of each small circle
float area = (radius) * (radius) * PI; //area of each small circle
float total = 0; //total areas of circles already drawn
float offset = frameCount * 0.01; //HOW DOES IT WORK & WHAT DOES IT DO
for (int i = 1; i <= circlenumber; ++i) { // loops through all of the circles making up the pattern
float angle = i*19 + offset; //HOW DOES IT WORK & WHAT DOES IT DO
total += area; // adds up the areas of all the small circles that have already been drawn
float amplitude = sqrt( total / PI ); //amplitude of trigonometric spiral
float x = width/2 + cos(angle) * amplitude;//HOW DOES IT WORK & WHAT DOES IT DO
float hue = i;//determines circle color based on circle number
fill(hue, 44, 255);//fills circle with that color
stroke(hue,44,255);
if(i%30 == 0)
line(0,i,width,i);
ellipse(x, i, radius*2, radius*2); //draws circle
}
}
Hopefully this helps clarify some of the issues with understanding.
I've made a lighting engine which allows for shadows. It works on a grid system where each pixel has a light value stored as an integer in an array. Here is a demonstration of what it looks like:
The shadow and the actual pixel coloring works fine. The only problem is the unlit pixels further out in the circle, which for some reason makes a very interesting pattern(you may need to zoom into the image to see it). Here is the code which draws the light.
public void implementLighting(){
lightLevels = new int[Game.WIDTH*Game.HEIGHT];
//Resets the light level map to replace it with the new lighting
for(LightSource lightSource : lights) {
//Iterates through all light sources in the world
double circumference = (Math.PI * lightSource.getRadius() * 2),
segmentToDegrees = 360 / circumference, distanceToLighting = lightSource.getLightLevel() / lightSource.getRadius();
//Degrades in brightness further out
for (double i = 0; i < circumference; i++) {
//Draws a ray to every outer pixel of the light source's reach
double radians = Math.toRadians(i*segmentToDegrees),
sine = Math.sin(radians),
cosine = Math.cos(radians),
x = lightSource.getVector().getScrX() + cosine,
y = lightSource.getVector().getScrY() + sine,
nextLit = 0;
for (double j = 0; j < lightSource.getRadius(); j++) {
int lighting = (int)(distanceToLighting * (lightSource.getRadius() - j));
double pixelHeight = super.getPixelHeight((int) x, (int)y);
if((int)j==(int)nextLit) addLighting((int)x, (int)y, lighting);
//If light is projected to have hit the pixel
if(pixelHeight > 0) {
double slope = (lightSource.getEmittingHeight() - pixelHeight) / (0 - j);
nextLit = (-lightSource.getRadius()) / slope;
/*If something is blocking it
* Using heightmap and emitting height, project where next lit pixel will be
*/
}
else nextLit++;
//Advances the light by one pixel if nothing is blocking it
x += cosine;
y += sine;
}
}
}
lights = new ArrayList<>();
}
The algorithm i'm using should account for every pixel within the radius of the light source not blocked by an object, so i'm not sure why some of the outer pixels are missing.
Thanks.
EDIT: What I found is, the unlit pixels within the radius of the light source are actually just dimmer than the other ones. This is a consequence of the addLighting method not simply changing the lighting of a pixel, but adding it to the value that's already there. This means that the "unlit" are the ones only being added to once.
To test this hypothesis, I made a program that draws a circle in the same way it is done to generate lighting. Here is the code that draws the circle:
BufferedImage image = new BufferedImage(WIDTH, HEIGHT,
BufferedImage.TYPE_INT_RGB);
Graphics g = image.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, WIDTH, HEIGHT);
double radius = 100,
x = (WIDTH-radius)/2,
y = (HEIGHT-radius)/2,
circumference = Math.PI*2*radius,
segmentToRadians = (360*Math.PI)/(circumference*180);
for(double i = 0; i < circumference; i++){
double radians = segmentToRadians*i,
cosine = Math.cos(radians),
sine = Math.sin(radians),
xPos = x + cosine,
yPos = y + sine;
for (int j = 0; j < radius; j++) {
if(xPos >= 0 && xPos < WIDTH && yPos >= 0 && yPos < HEIGHT) {
int rgb = image.getRGB((int) Math.round(xPos), (int) Math.round(yPos));
if (rgb == Color.white.getRGB()) image.setRGB((int) Math.round(xPos), (int) Math.round(yPos), 0);
else image.setRGB((int) Math.round(xPos), (int) Math.round(yPos), Color.red.getRGB());
}
xPos += cosine;
yPos += sine;
}
}
Here is the result:
The white pixels are pixels not colored
The black pixels are pixels colored once
The red pixels are pixels colored 2 or more times
So its actually even worse than I originally proposed. It's a combination of unlit pixels, and pixels lit multiple times.
You should iterate over real image pixels, not polar grid points.
So correct pixel-walking code might look as
for(int x = 0; x < WIDTH; ++x) {
for(int y = 0; y < HEIGHT; ++y) {
double distance = Math.hypot(x - xCenter, y - yCenter);
if(distance <= radius) {
image.setRGB(x, y, YOUR_CODE_HERE);
}
}
}
Of course this snippet can be optimized choosing good filling polygon instead of rectangle.
This can be solved by anti-aliasing.
Because you push float-coordinate information and compress it , some lossy sampling occur.
double x,y ------(snap)---> lightLevels[int ?][int ?]
To totally solve that problem, you have to draw transparent pixel (i.e. those that less lit) around that line with a correct light intensity. It is quite hard to calculate though. (see https://en.wikipedia.org/wiki/Spatial_anti-aliasing)
Workaround
An easier (but dirty) approach is to draw another transparent thicker line over the line you draw, and tune the intensity as needed.
Or just make your line thicker i.e. using bigger blurry point but less lit to compensate.
It should make the glitch less obvious.
(see algorithm at how do I create a line of arbitrary thickness using Bresenham?)
An even better approach is to change your drawing approach.
Drawing each line manually is very expensive.
You may draw a circle using 2D sprite.
However, it is not applicable if you really want the ray-cast like in this image : http://www.iforce2d.net/image/explosions-raycast1.png
Split graphic - gameplay
For best performance and appearance, you may prefer GPU to render instead, but use more rough algorithm to do ray-cast for the gameplay.
Nonetheless, it is a very complex topic. (e.g. http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-16-shadow-mapping/ )
Reference
Here are more information:
http://what-when-how.com/opengl-programming-guide/antialiasing-blending-antialiasing-fog-and-polygon-offset-opengl-programming/ (opengl-antialias with image)
DirectX11 Non-Solid wireframe (a related question about directx11 with image)
I am making a game in Android that requires a list of sprites to running from right to left so I try to flip the image using the code below.
It slows the game speed down so much, moves fast running to the right but slows down so much running to the left.
public void Draw(Canvas spriteBatch, int X, int Y, int imageIndex,int flip)
{
int drawY = (imageIndex / columns);
int drawX = imageIndex - drawY * columns;
int spriteX = drawX * spriteWidth;
int spriteY = drawY * spriteHeight;
Rect src = new Rect( spriteX, spriteY,spriteX + spriteWidth, spriteY + spriteHeight);
Rect dst = new Rect(X, Y, X + spriteWidth,Y + spriteHeight);
location.X = X;
location.Y = Y;
if(flip == 1)
{
//here image is flipped
spriteBatch.save();
spriteBatch.scale(-scaleX, scaleY, X, Y);
spriteBatch.drawBitmap(texture2D,src,dst, paint);
spriteBatch.restore();
//Use simple use this to flip image canvas.scale(-1, 1)
}
else if(flip == 0)
{
//draws sprite without flipping
spriteBatch.save();
spriteBatch.scale(scaleX, scaleY, X, Y);
spriteBatch.drawBitmap(texture2D,src,dst, paint);
spriteBatch.restore();
}
this.SetFrame(imageIndex);
}
I can flip using matrix but I can't draw a sprite using matrix.
Is there a way to draw sprite using matrix and would it make it faster?
matrix.reset();
matrix.setTranslate(location.X, location.Y);
matrix.setScale(-scaleX,scaleX);
matrix.postTranslate(location.X + texture2D.getWidth(), location.Y);
Or is there another way that is faster?
So I think that the matrix solution should will work perfectly the canvas object includes it's own built in matrix which if you manipulate will affect the output of all graphics after that point. So you just set the matrxi and then do the draw.
You're solution is okay but you're doing it wrong - rather do this part ONCE:
Create the left-ward facing sprite as you do at the beginning and then store it. Then EACH FRAME: Use this 'cached' copy of the inverted bitmap that you created.
A third solution (which I haven't tried but might work) is to manipulate the destination rectangle so that the left and right edges are swapped - the canvas docs say, "Draw the specified bitmap, scaling/translating automatically to fill the destination rectangle." - I suspect that this might include negative scaling to fit the space where the right edge is smaller than the left although the docs do not explicitly say so.
I have problem, that balls that move too fast can fly through wall (walls are 4 pixels wide, and speed of ball occasionally is more than 400 pixels per second (which is more than 4 pixels per update assuming fps is 60)). I researched it on StackOverflow, but the solution for others is not suitable for me, as they are using rectangles, and i am using pixel collision. Here is method which returns if ball intersects with wall (method is in Ball class):
public boolean intersects(Wall w) {
BufferedImage im1 = new BufferedImage (size, size, BufferedImage.TYPE_INT_ARGB); // size is diameter of the ball
BufferedImage im2 = new BufferedImage (size, size, BufferedImage.TYPE_INT_ARGB);
Graphics2D g1 = im1.createGraphics();
Graphics2D g2 = im2.createGraphics();
g1.translate(-x + size/2, -y + size/2);
g2.translate(-x + size/2, -y + size/2);
render(g1);
w.render(g2);
g1.dispose();
g2.dispose();
for (int x = 0; x < im1.getWidth(); x++){
for (int y = 0; y < im1.getHeight(); y++){
Color c1 = new Color(im1.getRGB(x, y), true);
Color c2 = new Color(im2.getRGB(x, y), true);
if (c1.getAlpha() != 0 && c2.getAlpha() != 0){
return true;
}
}
}
return false;
}
Here is how ball is drawn:
public void render(Graphics2D g) {
color = new Color (Color.HSBtoRGB(hue, 0.5f, 0.5f));
g.setColor (color);
g.fillOval((int)(x-size/2), (int) (y-size/2), size, size);
}
The wall is simply defined as 2 points, and here is how wall is drawn:
public void render(Graphics2D g2) {
g2.setColor(new Color(r, g, b));
g2.setStroke(new BasicStroke(width)); //width = 4
g2.draw(new Line2D.Float(p1.x, p1.y, p2.x, p2.y));
}
I have a couple of quick ideas that you might want to attempt.
Why not check the endpoints against the dimensions of the wall (some simple algebra and I think what you might already be trying to do). If you need help with the algebra involved I'll include a link below. Basically just record the starting point of the ball (prior to moving), and on update check the old location versus the estimated location, and run a function to see if a collision occurs. This will become tricky however if you want realistic physics.
https://gamedev.stackexchange.com/questions/26004/how-to-detect-2d-line-on-line-collision
In the above link, just assume your walls are one line, and the previous coordinates and the expected next coordinates of the ball form the other line. This method works very well if you only have the ball moving in straight lines.
Your only alternative might be to have two simultaneous models running (basically a visual space which you have in showing the ball and walls, and a virtual setup checking the physics behind what you are showing.
This script draws the controls, hero, surface and the map:
public void render(Canvas canvas) {
canvas.drawColor(Color.TRANSPARENT);
Drawable myImage;
int tileWidth = 50;
int tileHeight = 50;
int rowBaseX = 0;
int rowBaseY = 0;
int[][] board = new int[][] {
{0,0,0,0,0,0,2,0,0,0,},
{0,0,0,0,0,2,2,0,0,0,},
{0,0,0,0,0,2,0,0,0,0,},
{0,0,0,0,0,2,0,0,0,0,},
{0,0,0,2,2,2,0,0,0,0,},
{0,0,0,2,0,0,0,0,0,0,},
{0,0,0,2,0,0,0,0,0,0,},
{0,0,2,2,0,0,0,0,0,0,},
{0,0,2,0,0,0,0,0,0,0,},
{0,0,2,0,0,0,0,0,0,0,}
};
int mapWidth = 10;
int mapHeight = 10;
for (int row = 0; row < mapHeight; row++)
{
for (int col = 0; col < mapWidth; col++)
{
Resources res = this.getContext().getResources();
switch(board[row][col])
{
case 0:
myImage = res.getDrawable(R.drawable.tile1);
break;
case 1:
myImage = res.getDrawable(R.drawable.tile2);
break;
default:
myImage = res.getDrawable(R.drawable.tile3);
break;
}
int curL = rowBaseX + (col * tileWidth);
int curU = rowBaseY + (row * tileHeight);
int curR = curL + tileWidth;
int curD = curU + tileHeight;
myImage.setBounds(curL,curU,curR,curD);
myImage.draw(canvas);
}
}
droid.draw(canvas);
butt.draw(canvas);
butt1.draw(canvas);
butt2.draw(canvas);
butt3.draw(canvas);
buttz.draw(canvas);
buttz1.draw(canvas);
buttz2.draw(canvas);
buttz3.draw(canvas);
buttx.draw(canvas);
}
There is a hero, which has to be redrawn when player moves him with the controls, and all other drawables also has to be redrawn. The problem is that drawing a map is a long process, so the bigger map i create, the slower hero moves, because every tile of the map has to be painted. Is there a way to put all the tiles to a one bitmap in other method and draw that one bitmap in the canvas method?
If creating a static map outside your application is not an option, you could separate the drawing of static content from the dynamic content.
Create a Bitmap, create a Canvas with it and draw your map on that Canvas. You only need to do this once. Render the dynamic content once per frame on another Canvas.
Then draw both Bitmaps on the "real" Canvas of your SurfaceView. It could look like this :
the once-per-map part :
Bitmap mapBitmap = Bitmap.createBitmap(width, height, myConfig);
Canvas c = new Canvas(mapBitmap);
//draw map on c
...and the once-per-frame part:
//draw dynamic stuff
Canvas canvas = getHolder().lockCanvas();
canvas.drawBitmap(mapBitmap, null, targetRect, null);
canvas.drawBitmap(heroBitmap, null, targetRect, null);
getHolder().unlockCanvasAndPost();
EDIT:
You draw your tiles like you used to with myImage.draw(canvas), but passing the mapBitmap-Canvas as argument instead of your "real" canvas. myConfig has to be a Bitmap.Config. Take the RGB_565 as it is the internal format.
The best option is to only draw the portion of your map that is visible on the screen. That way no matter how big the overall map becomes the drawing of that map is always constant. Since you're on a grid system you can easily figure out which cell the hero is in:
heroGridX = hero.x % mapWidth;
heroGridY = hero.y % mapHeight;
From there you can calculate how many cells around the player you want to draw by using the width and height of the canvas and the constant size of your grid cell's width and height:
leftGrid = heroGridX - (canvas.getWidth() / tileWidth) / 2;
topGrid = heroGridY - (canvas.getHeight() / tileHeight) / 2;
rightGrid = heroGridX + (canvas.getWidth() / tileWidth) / 2;
bottomGrid = heroGridY + (canvas.getHeight() / tileHeight) / 2;
You could use a data structure to store these values independent of the hero and only move them once the player gets close to an edge to scroll the map. That way the hero moves up and down without scrolling the map until they get X or Y pixels to the edge of a tile. Instead of calculating these inside the rendering routine.
This will use far less memory than drawing the entire map into one large bitmap. Drawing into a large bitmap is trading more memory usage for less CPU time. As your map grows larger so does the memory needed to draw that map. This algorithm merely keeps drawing the map a constant because the size of your screen doesn't change at runtime. And, in comparison to the other option your memory usage doesn't grow any larger as your map grows larger (it grows very small in comparison to drawing more tiles in a canvas). One important fact about this is that if you did get a larger screen (say a tablet vs a phone). This algorithm will scale up properly too so the player will see more of the surrounding terrain of the map.