I'm using Libgdx for a project and more precisely Box2DLights.
My problem is the following one : When I want to put a new "PointLight" it's always on the center of the screen. And if I change the coordinates, it doesn't work.
Inside my "show()" method :
Box2D.init();
world = new World(new Vector2(0, 0), true);
rh = new RayHandler(world);
rh.setAmbientLight(1.2f, 0.2f, 0.2f, 0.1f);
pl = new PointLight(rh, 100, new Color(1,1,1,1),(float) 0.5,0,0);
Inside my "render()" method :
Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
world.step(delta, 8, 3);
renderer.begin(ShapeType.Filled);
for (SolarSystem ss : solarSystemList)
{
if(ss.getColor() <= 15) colorSS = Color.YELLOW;
else if(ss.getColor() > 15 && ss.getColor() < 31) colorSS = Color.ORANGE;
else if(ss.getColor() > 30 && ss.getColor() < 46) colorSS = Color.RED;
else if(ss.getColor() > 45) colorSS = Color.CYAN;
renderer.setColor(colorSS);
renderer.circle(ss.getMapX(), ss.getMapY(), ss.getSize() - 3);
}
renderer.end();
rh.updateAndRender();
Result :
Now if I try to change coordinates :
pl = new PointLight(rh, 100, new Color(1,1,1,1),(float) 0.5, 50, 50);
... no light anymore
Do you know how it's possible to put the light where I want ?
EDIT : My screen size : width - 860px / height - 645px
if the (1,1) is the top right and the (0,0) is bottom left and the (0.5,0.5) is the middle of the screen, then i propose to do this :
insert the value that you want and divide it by the width and height of of your screen for example
( xPosition/Gdx.graphics.width, yPosition/Gdx.graphics.height )
Update :
sorry i didn't see that (0,0) was the center so i propse to you to use this instead :
width = Gdx.graphics.width;
height = Gdx.graphics.height;
((xPosition - width/2)/ width/2 , (yPosition - height/2)/ height/2)
Update 2 :
i think you are doing little arithmetic mistake assume that your
width = 860 and your height = 645 as you said
this is the equation :
x= ((xPosition - width/2)/ width/2)
y= (yPosition - height/2)/ height/2)
x = (50 - 860/2) / (860/2)
y = (50 - 645/2) / (645/2)
x = (50 - 430) / (430)
y = (50 - 322.5) / (322.5)
x = (50 - 430) / (430) = (-380) / (430)
y = (50 - 322.5) / (322.5) = (-272.5) / (322.5)
x = -0.88
y = -0.84
which is closer to (-1,-1) aka : the left bottom corner
hope it was helpful :)
If you take a distance of 0.5 and your light shines above half of the screen I just assume that a position of 50, 50 will not fit into this screen. Just try to change your position to a smaller value. Maybe your coordinates do not represent pixels but other units as it is recommended for box2d.
Edit: As I don't know your entire libgdx app I just recommend to have a deeper look into Camera, ViewPort and such. For example the box2dlights RayHandler can get your camera via setCombinedMatrix. You may also want to synchronize lights with bodies and the box2d world with your sprites.
Related
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)
My pong ball has x and y values from 0.0 to 1.0, which are the bounds the screen.
It bounces off the screen just fine, but won't recognize my paddle. My paddles have values outside the range detectable, I don't know how to get the values to be between 0.0 and 1.0.
I need to get the values to be between 0.0 and 1.0
float paddleHeightTop = (float)(rPaddle - (height/10));
float paddleHeightBottom = (float)(rPaddle + (height/10));
float paddleLeadingEdge = (float) (rPaddle/1000);
paddleLeadingEdge has value 897.0, needs to be 0.897... Refuses to convert, remains 897.
I haven't worked out the math on the paddleHeightTop or Bottom, but they also need to have a value between 0.0 and 1.0...
paddleHeightTop has value 183.0.
paddleHeightBottom has value 1.8970001.
rPaddle updates it's value from on 'onMotionEvent'.
My code for onDraw for paddle looks like this (and draws and updates position correctly):
canvas.drawRect( 93 * (width / 100) , rPaddle - (height/10), 95 * (width / 100), rPaddle + (height/10), light);
I'm putting that here because I'm sure there some relationship I'm missing.
Thanks ahead of time.
PS:
This is my collision detector (moved from comments):
if (ballY < paddleHeightTop && ballX > paddleLeadingEdge && ballY > paddleHeightBottom ) {
soundPool.play(paddleSound, 1, 1, 0, 0, 1);
ballSpeedX *= -1;
}
My Android Studio Debugger was having an error, never mind.
i have created a 10x10 grid in the center of the screen in android, now i would like to give each square in the grid a coordinate. for example top left square in the grid would be 0 then 1, 2,3 and so on. But i dont know how to do this. i am trying to do this in a draw class which extends view. my code of what i am trying is below
public int coordinates(int posX, int posY){
int startX = (screenWidth / 2) - (rectSide / 2);
int startY = (screenHeight / 2) - (rectSide / 2);
//for(int i=0; i<=10000; i+=100){
xCoord = (startX + (posX*100));
yCoord = (startY + (posY*100));
}
You know you start at point 0,0 top left. So assuming you have equally spaces squares you can just do the screen height / 10 to get how far apart each square should be in the y direction. And then do the same for the x direction. Say your screen was 1000 pixels tall.
Then your grid at position (0,1) would be at (0,100) pixels. (0,2) would be (0,200) you are just multiplying the y coordinate by the height of each square in the grid.
I try to find the way to rotate the LinearGradient object nested into e.g. Rectangle object, say:
Rectangle rect = new Rectangle(0, 0, 200, 200);
LinearGradient lg = new LinearGradient(0, 0, 100, 0, false, CycleMethod.REPEAT, new Stop[] {
new Stop(0, Color.BLACK);
new Stop(0.5, Color.WHITE);
new Stop(1, Color.BLACK);
});
rect.setFill(lg);
Now, I try to rotate this lg object, for example for 45 degrees to the left, but without rotating the whole rect. Is there any way to achieve that?
The first parameters that are given to the LinearGradient constructor are the coordinates of the start- and end point of the gradient axis, respectively. This means that you can achieve a "rotated" gradient simply by passing in an appropriately rotated axis.
In the simplest form, for the example that you described, you can use the following pattern:
double angleInRadians = Math.toRadians(45);
double length = 100;
double endX = Math.cos(angleInRadians) * length;
double endY = Math.sin(angleInRadians) * length;
LinearGradient lg = new LinearGradient(0, 0, endX, endY, ...);
This will result in a gradient rotated by 45 degrees.
The fixed values here will affect the final appearance of the gradient, together with the other parameters. Referring to your example, this gradient with the same "wave length" as before (namely 100), and start with the same color at the upper left corner (i.e. Color.BLACK will be at coordinates (0,0)).
Trig ratios can be used for a more flexible gradient angle. Please note: It does not implement repeat, hence add more stops in the gradient object.
private fun createGradient(width: Float, height: Float): LinearGradient {
val mode = TileMode.CLAMP
val angleInRadians = Math.toRadians(mAngle.toDouble())
val halfWidth = width / 2
val halfHeight = height / 2
val sinAngle = sin(angleInRadians)
val cosAngle = cos(angleInRadians)
val x0 = (halfWidth * (1 + sinAngle)).toFloat()
val y0 = (halfHeight * (1 - cosAngle)).toFloat()
val x1 = (halfWidth * (1 - sinAngle)).toFloat()
val y1 = (halfHeight * (1 + cosAngle)).toFloat()
return LinearGradient(x0, y0, x1, y1, mGradient, null, mode)
}
This question already has answers here:
Best way to detect the touched object (moving) from collection in libgdx
(2 answers)
Closed 8 years ago.
I started using libGDX soon, and I was making a game that has water droplets falling from the top of the screen and the user should tap them before they do touch the bottom of the screen. I have a problem in knowing if the user did tap the droplet to do something.
This is my Render method:
Gdx.gl.glClearColor(0/255.0f, 0/255.0f, 100/255.0f, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
cam.update();
if (TimeUtils.millis() - lastDrop > 333)
spawnRainDrop();
Iterator<Rectangle> iter = raindrops.iterator();
while (iter.hasNext()) {
Rectangle raindrop = iter.next();
raindrop.y -= 300 * Gdx.graphics.getDeltaTime();
if (raindrop.y + 64 < 0) {
iter.remove();
}
if (Gdx.input.isTouched()) {
Vector3 touchPos = new Vector3();
cam.unproject(touchPos);
if (Gdx.input.getX() == raindrop.x + 64 / 2 && Gdx.input.getY() == raindrop.y + 64 / 2) {
System.out.println("Tapped");
}
}
}
The tapping code doesn't seem to work.
I would really appreciate if someone did explain their answer.
Thanks
You are testing for touch at a single point and not in a bounding box around your object. Use the following to test for touch inside a bounding box:
if(Gdx.input.isTouched()) {
Vector3 touchPos = new Vector3();
cam.unproject(touchPos);
float halfWidth = 64 / 2.0f;
float halfHeight = 64 / 2.0f;
if( Gdx.input.getX() >= raindrop.x - halfWidth &&
Gdx.input.getX() <= raindrop.x + halfWidth &&
Gdx.input.getY() <= raindrop.y + halfHeight &&
Gdx.input.getY() >= raindrop.y - halfHeight ) {
System.out.println("Tapped");
}
}
I'm assuming a bottom/left origin so just change the signs accordingly if you use a different one.
In two dimensions, I used the following approach to detect where the player clicked: https://stackoverflow.com/a/24511980/2413303
But in your case, I think the problem is just that you used Gdx.input.getX() and Gdx.input.getY() instead of the coordinates you've obtained from cam.unproject(touchPos), according to this other answer on the same question: https://stackoverflow.com/a/24503526/2413303 .