I'm adding lighting to my XNA 2D tile based game.
I found this article useful, but the way its done it does not support collision. What I'd like is a method to do the following
Have always lit point
Collision (If the light ray hits a block, then dim the next block by whatever amount until its dark to simulate shadows)
I've been searching around for quite a while but no luck (I did find Catalin's tutorial, but it seemed a bit advanced for me, and didn't apply to tiles well due to redrawing the entire game for each point)
I'll share my method for applying a smooth lighting effect to a 2D tile grid. ClassicThunder's answer provides a nice link for shadows.
First off, we will need to calculate the lighting values of each tile which will be blurred later. Let me illustrate how this works before I get into the code.
Basicly what we do is loop through all the tiles, starting from the top, if a tile is blank, set the CurrentLight variable to max brightness, if we find a solid tile, set it as the CurrentLight variable and subtract an "absorbsion" amount from the CurrentLight. This way, on our next solid tile, when we set the tile to the CurrentLight value, it will be slightly less. This process is repeated until the array is iterated.
Now there will be a nice top to bottom lighting effect, but it isn't that great. We must repeat this process 3 more times, for bottom to top, left to right, and right to left. And it can be repeated more times for better quality.
Basically running this code on every tile in the loop
if (tile.Light > CurrentLight) //If this tile is brighter than the last, set the current light to the tiles light
CurrentLightR = tile.Light;
else if (CurrentLight != 0f) //If it is less, and isnt dark, then set the tile to the current light
tile.Light = CurLightR;
if (tile.Light == CurLightR) //If it is the same, subtract absorb values
CurrentLight -= tile.Absorb;
And there you go, nice tile lighting. However if you want a less "pixelized" look, you can check out my question on gamedev for that.
For per-pixel lighting, you might have to look somewhere else, because I don't know about that.
For per-tile lighting,
in SpriteBatch.draw, a few of the overloaded methods takes a color. When you use Color.White, the sprite that the SpriteBatch draws is normal colored.
Use Color multiplication by creating a new Color(Color.yourcolor.r*float, Color.yourcolor.y*float, Color.yourcolor.z*float, 255)
Basically, to get the float, try to find out a formula that calculates the brightness of the block due to nearby lights (stored in an array or list, probably). Since there's no normals needed for 2D games, this formula should be relatively easy.
Related
I have a game in Unity.
The idea of the game is that an object is spawned on the screen.
It exists for several seconds.
You need to click on it, while it exists.
What I want to do, is that while it exists - I want to show a timer, but not with numbers, but with an outlined line on the perimeter of the screen.
I know, how to make a make a slider and how to make circular health bar, yet I do not really have an idea to make a slider, that is decresing from everywhere to the centre as you can see on the screenshots below.
I would be thankful for the inspiration!
This should be a very good fit using alpha cutoff with a custom shader.
Here's a video explaining it in detail (it's only the first half you will need).
The basic idea is to create a texture with a graded alpha depending on how you want the texture to appear/disappear. In this case the alpha value at the top and the bottom would be close to 0 and then gradually around the line on both sides towards the middle increase to 1. The shader then cuts off the texture below a cutoff threshold which you can change depending on the value of the timer.
As a dumb and simple approach:
Have two images with fill, one goes middle towards top the other middle towards bottom.
Just imagine the pnguin is your outline ^^
I had the idea to create a game where you would see everything like a bat. There are some people which have achieved something like that.
The problem is I would like to have another type of "Bat Vision". It is pretty hard to explain but if i look into one direction and then rotate the character I would still see something I looked at before.
Explanation with timestamps:
Sec 0: I am looking at point A where a object is 300 meters away
Sec 1: I rotate to point B and see an object 100 meters away
Sec 2: I see object A and B at the same time
Sec 3: I still see object B
Explanation with picture:
As you can see the player first shoots the ultrasonic waves a first on A and afterwards waves b on B. They both come back at the same time so that both objects are seen at the same time.
If you did not understand why I see objects delayed. As everyone knows sound travels with a speed about 1/3 km/s. So an object which is 300 meters in the distance would get seen after about 2 sec.
I already tried to shoot RayCasts or GameObjects into the direction and as further they are away the darker the pixel at that position will be. But as you can think... Shooting 2 million RayCasts every Frame is not very healthy. I was also thinking about Shaders but I don't know how it is possible that one still sees the Shaders while looking into another direction. Also I don't have much experience with Shaders and would need help there anyways.
I would be glad if someone has another Idea how I could create this.
To achieve this the easiest way would be using an image effect shader that accesses the depth buffer.
The value of each pixel in your depth buffer is by definition the distance of that pixel in the world to the camera
You can decrease the value of each pixel by some value every second and set the value to max every X seconds based on the distance given by the depth buffer... this will give the effect of sound bouncing off that object back to your player
https://www.youtube.com/watch?v=OKoNp2RqE9A
tweak the colors to achieve needed effect
It appears to me that maybe you want to duplicate any meshes and surfaces that your player has 'seen', and keep this in view with some grayish light settings. And the 'real' objects are all hidden, that is, they all have their MeshRenderer components disabled or removed.
If the player shall see an item like a shadow in front of him even though he moves, you could have the cloned visible duplicated move along with the player some time until they fade.
To find out what to duplicate I am sure you'd need a lot less raycasts than doing it for each pixel. Clone the mesh, then check visibility for each vertex and set the visibility in the duplicated vertex color and alpha.
How do I remove this "giggly" effect when slowly moving a sprite?
I have tried adjusting Antialiasing values in QualitySettings and Filter Mode in ImportSettings in the Unity Editor but that doesn't change anything.
Ideally, I would like to keep the Filter Mode to Point (no filter) and anti aliasing turned on to 2x
The sprite is located inside a Sprite Renderer component of a GameObject.
I have uploaded my Unity Project here: http://www.filedropper.com/sprite
I really don't know how to fix the problem... Can anyone help with my personal project?
I cooked up a quick animation to demonstrate what's happening here:
The grid represents the output pixels of your display. I've overlaid on top of it the sliding sprite we want to sample, if we could render it with unlimited sub-pixel resolution.
The dots in the center of each grid cell represent their sampling point. Because we're using Nearest-Nieghbour/Point filtering, that's the only point in the texture they pay attention to. When the edge of a new colour crosses that sampling point, the whole pixel changes colour at once.
The trouble arises when the source texel grid doesn't line up with our output pixels. In the example above, the sprite is 16x16 texels, but I've scaled it to occupy 17x17 pixels on the display. That means, somewhere in every frame, some texels must get repeated. Where this happens changes as we move the sprite around.
Because each texel is rendered slightly larger than a pixel, there's a moment where it completely bridges the sampling points of two adjacent pixels. Both sampling points land within the same enlarged texel, so both pixels see that texel as the nearest one to sample from, and the texel gets output to the screen in two places.
In this case, since there's only a 1/16th scale difference, each texel is only in this weird situation for a frame or two, then it shifts to its neighbour, creating a ripple of doubled pixels that appears to slide across the image.
(One could view this as a type of moiré pattern resulting from the interaction of the texel grid and the sampling grid when they're dissimilar)
The fix is to ensure that you scale your pixel art so each texel is displayed at the size of an integer multiple of pixels.
Either 1:1
Or 2:1, 3:1...
Using a higher multiple lets the sprite move in increments shorter than its own texel size, without localized stretching that impacts the intended appearance of the art.
So: pay close attention to the resolution of your output and the scaling applied to your assets, to ensure you keep an integer multiple relationship between them. The blog post that CAD97 links has practical steps you can take to achieve this.
Edit: To demonstrate this in the Unity project you've uploaded, I modified the camera settings to match your pixels to units setting, and laid out the following test. The Mario at the top has a slightly non-integer texel-to-pixel ratio (1.01:1), while the Mario at the bottom has 1:1. You can see only the top Mario exhibits rippling artifacts:
You might be interested in this blog post about making "pixel-perfect" 2D games in Unity.
Some relevant excerpts:
If you start your pixel game with all the default settings in Unity, it will look terrible!
The secret to making your pixelated game look nice is to ensure that your sprite is rendered on a nice pixel boundary. In other words, ensure that each pixel of your sprite is rendered on one screen pixel.
These other settings are essential to make things as crisp as possible.
On the sprite:
Ensure your sprites are using lossless compression e.g. True Color
Turn off mipmapping
Use Point sampling
In Render Quality Settings:
Turn off anisotropic filtering
Turn off anti aliasing
Turn on pixel snapping in the sprite shader by creating a custom material that uses the Sprite/Default shader and attaching it to the SpriteRenderer.
Also, I'd just like to point out that Unless you are applying Physics, Never Use FixedUpdate. Also, if your sprite has a Collider and is moving, it should have a Kinematic RigidBody attached even if you're never going to use physics, to tell the engine that the Collider is going to move.
Same problem here. I noticed that the camera settings and scale are also rather important to fix the rippling problem.
Here is What Worked for me:
Go to Project Settings > Quality
Under Quality Make the default Quality as High for all.
Set the Anistropic Texture to "Disabled"
Done, And the issue is resolved for me.
Image Reference:
enter image description here
I have a map, containing many objects in an area sized 5000*5000.
my screen size is 800*600.
how can i scroll my map, i don't want to move all my objects left and right, i want the "camera" to move, But unfortunately i didn't found any way to move it.
Thanks
I think you are looking for the transformMatrix parameter to SpriteBatch.Begin (this overload).
You say you don't want the objects to move, but you want the camera to move. But, at the lowest level, in both 2D and 3D rendering, there is no concept of a "camera". Rendering always happens in the same region - and you must use transformations to place your vertices/sprites into that region.
If you want the effect of a camera, you have to implement it by moving the entire world in the opposite direction.
Of course, you don't actually store the moved data. You just apply an offset when you render the data. Emartel's answer has you do that for each sprite. However using a matrix is cleaner, because you don't have to duplicate the code for every single Draw - you just let the GPU do it.
To finish with an example: Say you want your camera placed at (100, 200). To achieve this, pass Matrix.CreateTranslation(-100, -200, 0) to SpriteBatch.Begin.
(Performing a frustum cull yourself, as per emartel's answer, is probably a waste of time, unless your world is really huge. See this answer for an explanation of the performance considerations.)
Viewport
You start by creating your camera viewport. In the case of a 2D game it can be as easy as defining the bottom left position where you want to start rendering and expand it using your screen resolution, in your case 800x600.
Rectangle viewportRect = new Rectangle(viewportX, viewportY, screenWidth, screenHeight);
Here's an example of what your camera would look like if it was offset off 300,700 (the drawing is very approximate, it's just to give you a better idea)
Visibility Check
Now, you want to find every sprite that intersects the red square, which can be understood as your Viewport. This could be done with something similar to (this is untested code, just a sample of what it could look like)
List<GameObject> objectsToBeRendered = new List<GameObject>();
foreach(GameObject obj in allGameObjects)
{
Rectangle objectBounds = new Rectangle(obj.X, obj.Y, obj.Width, obj.Height);
if(viewportRect.IntersectsWith(objectBounds))
{
objectsToBeRendered.Add(obj);
}
}
Here's what it would look like graphically, the green sprites are the ones added to objectsToBeRendered. Adding the objects to a separate list makes it easy if you want to sort them from Back to Front before rendering them!
Rendering
Now that we found which objects were intersecting we need to figure out where on the screen the will end up.
spriteBatch.Begin();
foreach(GameObject obj in objectsToBeRendered)
{
Vector2 pos = new Vector2(obj.X - viewportX, obj.Y - viewportY);
spriteBatch.Draw(obj.GetTexture(), pos, Color.White);
}
spriteBatch.End();
As you can see, we deduce the X and Y position of the viewport to bring the world position of the object into Screen Coordinates within the viewport. This means that the small square that could be at 400, 800 in World Coordinates would be rendered at 100, 100 on the screen given the viewport we have here.
Edit:
While I agree with the change of "correct answer", keep in mind that what I posted here is still very useful when deciding which animations to process, which AIs to update, etc... letting the camera and the GPU make the work alone prevents you from knowing which objects were actually on screen!
Currently as a trail effect in my game I have for every 5 frames a translucent texture copy of a sprite is added to a List<> of trails.
The alpha values of these trails is decremented every frame and a draw function iterates through the list and draws each texture. Once they hit 0 alpha they are removed from the List<>.
The result is a nice little trail effect behind moving entities. The problem is for about 100+ entities, the frame rate begins to drop drastically.
All trail textures come from the same sprite sheet so i dont think it's batching issue. I profiled the code and the CPU intensity is lower during the FPS drop spikes then it is at normal FPS so I assume that means its a GPU limitation?
Is there any way to achieve this effect more efficiently?
Heres the general code im using:
// fade alpha
m_alpha -= (int)(gameTime.ElapsedGameTime.TotalMilliseconds / 10.0f);
// draw
if (m_alpha > 0) {
// p is used to alter RGB of the trails color (m_tint) depending on alpha value
float p = (float)m_alpha/255.0f;
Color blend = new Color((int)(m_tint.R*p), (int)(m_tint.G*p), (int)(m_tint.B*p), m_alpha);
// draw texture to sprite batch
Globals.spriteBatch.Draw(m_texture, getOrigin(), m_rectangle, blend, getAngle(), new Vector2(m_rectangle.Width/2, m_rectangle.Height/2), m_scale, SpriteEffects.None, 0.0f);
} else {
// flag to remove from List<>
m_isDone = true;
}
I guess i should note, the m_texture given to the trail class is a reference to a global texture shared by all trails. Im note creating a hard copy for each trail.
EDIT: If I simply comment out the SpriteBatch.Draw call, even when im allocating a new trail every single frame for hundreds of objects there is no drop in frames... there has got to be a better way to do this.
Usually for trails, instead of clearing the screen on every frame, you simply draw a transparent screen-sized rectangle before drawing the current frame. Thus the previous frame is "dimmed" or "color blurred" while the newer frame is fully "clear" and "bright". As this is repeated, a trail is generated from all the previous frames, which are never cleared but rather "dimmed".
This technique is VERY efficient and it is used in the famous Flurry screensaver (www.youtube.com/watch?v=pKPEivA8x4g).
In order to make the trails longer, you simply increase the transparency of the rectangle that you use to clear the screen. Otherwise, you make it more opaque to make the trail shorter. Note, however, that if you make the trails too long by making the rectangle too transparent, you risk leaving some light traces of the trail that due to alpha blending, might not completely erase even after a long time. The Flurry screensaver suffers from this kind of artifact, but there are ways to compensate for it.
Depending on your situation, you might have to adapt the technique. For instance, you might want to have several drawing layers that allow certain objects to leave a trail while others don't generate trails.
This technique is more efficient for long trails than trying to redraw a sprite thousands of times as your current approach.
On the other hand, I think the bottleneck in your code is the following line:
Globals.spriteBatch.Draw(m_texture, getOrigin(), m_rectangle, blend, getAngle(), new Vector2(m_rectangle.Width/2, m_rectangle.Height/2), m_scale, SpriteEffects.None, 0.0f);
It is inefficient to have thousands of GPU calls like Draw(). It would be more efficient if you had a list of polygons in a buffer, where each polygon is located in the correct position and it has transparency information stored with it. Then, with a SINGLE call to Draw(), you can then render all polygons with the correct texture and transparency. Sorry I cannot provide you with code for this, but if you want to continue with your approach, this might be the direction you are headed. In short, your GPU can certainly draw millions of polygons at a time, but it can't call Draw() that many times...