jcsnider Posted October 4, 2015 Share Posted October 4, 2015 This has been requested wayyyy to many times so I am finally caving in. Here is a really simple tutorial for making basic spotlights in C# and VB.Net with SFML. If you are using another rendering library this may still help. VB.Net Guide Step 1 Start with a blank WinForms project, add references to the SFML .dlls and place the csfml dlls into the output folder of the application. Expand the generated Form1 a little bit and then double click it to open up the form code. Replace it with this startup code. Program.cs Imports SFML.Graphics Imports SFML.Window Public Class Form1   Dim WithEvents _window As RenderWindow   Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load     StartSFMLProgram()   End Sub   Private Sub StartSFMLProgram()     _window = New RenderWindow(New VideoMode(640, 480), "2D Spotlight Tutorial", Styles.Default)     _window.SetVisible(True)     While _window.IsOpen       _window.DispatchEvents()       _window.Clear(Color.White)       _window.Display()     End While   End Sub   Private Sub OnClosed(sender As Object, e As EventArgs) Handles _window.Closed     _window.Close()     Application.Exit()   End Sub End Class Step 2 Lets go ahead and add some graphics. I am going to be using these two... Save both of these images to your projects build folder! Right Click and Save As > "Character.png" and Right Click and Save As > "Map.png" Step 3 Now, lets add the code to load these graphics. Before: _window = New RenderWindow(New VideoMode(640, 480), "2D Spotlight Tutorial", Styles.Default) Add:     'Load our map and character textures. Attach then to drawable sprite objects.     Dim charTex As New Texture("Character.png")     Dim mapTex As New Texture("Map.png")     Dim charSprite As New Sprite(charTex)     charSprite.Position = New SFML.System.Vector2f(290, 150)     Dim mapSprite As New Sprite(mapTex) Step 4 The next step is to render our graphics to the screen. Before:       _window.Display() Add:         _window.Draw(mapSprite)         _window.Draw(charSprite) If you run your program you should see a window like this. So, how do we darken the window? Well, lets take a step back and thing about how we would do it outside of code. Making the map dark is easy. We can take a layer of black, make it semi transparent, and then throw it on top of the image like so. This is how you could do it using GIMP. Step 5 Lets do it via code! Lights often change, so we have to generate the dark texture and the lights at runtime. We can do this with a RenderTexture. After:     Dim mapSprite As New Sprite(mapTex) Add:     'Create a RenderTexture for our Dark/Night overlay     Dim darkTex As New RenderTexture(640, 480)     Dim darkSprite As New Sprite(darkTex.Texture)     darkTex.Clear(New Color(0, 0, 0, 200))     darkTex.Display() and After:         _window.Draw(charSprite) Add:         _window.Draw(darkSprite) Compile again, your window should look like this now: So, how do we add light!? We cannot render our dark texture and then render something light on top of it, or else we would get a terrible effect that looks like this. So, we have to cut-out piece of the dark texture before it is drawn to the game screen. This is done by multiplicative blending. Give me a moment to try to explain myself. Let's put this into numbers. I am telling you: Transparency  == 0 and White == 1 Bear with me for a moment while I explain. Remember the rules you were taught in elementary school? Any Number * 1 = Itself. Any Number * 0 = 0 We are going to use those same rules with our graphics. With that same logic, I am telling you that Transparency  * Black == Transparency White  * Black == Black Do you see the trick now? If not it's fine but we are going to put this concept to work. Lets say we had this image loaded as a texture.... and then we multiplied that somewhere over our darkness texture What would happen? The center of our light graphic is transparent, so multiplying would remove all color from our dark texture, while the white on the edges would multiply leaving black with a smooth transition in the middle. Something like the graphic below: Want to see it in action? Let's try it! Step 6 We need to add our new light texture! Right Click and Save this Image as > "Light.png" Add it to your projects build folder. Then in your code. After:       darkTex.Clear(new Color(0,0,0,200)) Add:     'Create the light texture to multiply over the dark texture.     Dim lightTex As New Texture("Light.png")     Dim lightSprite As New Sprite(lightTex)     lightSprite.Position = New SFML.System.Vector2f(156, 24)     'We are putting the light onto the drawTex by multiplying. Not drawing to the game window.     darkTex.Draw(lightSprite, New RenderStates(BlendMode.Multiply)) Now if you run the application you will see a nice spotlight effect! Final Product: Here is the full code too: Imports SFML.Graphics Imports SFML.Window Public Class Form1   Dim WithEvents _window As RenderWindow   Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load     StartSFMLProgram()   End Sub   Private Sub StartSFMLProgram()     'Load our map and character textures. Attach then to drawable sprite objects.     Dim charTex As New Texture("Character.png")     Dim mapTex As New Texture("Map.png")     Dim charSprite As New Sprite(charTex)     charSprite.Position = New SFML.System.Vector2f(290, 150)     Dim mapSprite As New Sprite(mapTex)     'Create a RenderTexture for our Dark/Night overlay     Dim darkTex As New RenderTexture(640, 480)     Dim darkSprite As New Sprite(darkTex.Texture)     darkTex.Clear(New Color(0, 0, 0, 200))     'Create the light texture to multiply over the dark texture.     Dim lightTex As New Texture("Light.png")     Dim lightSprite As New Sprite(lightTex)     lightSprite.Position = New SFML.System.Vector2f(156, 24)     'We are putting the light onto the drawTex by multiplying. Not drawing to the game window.     darkTex.Draw(lightSprite, New RenderStates(BlendMode.Multiply))     darkTex.Display()     _window = New RenderWindow(New VideoMode(640, 480), "2D Spotlight Tutorial", Styles.Default)     _window.SetVisible(True)     While _window.IsOpen       _window.DispatchEvents()       _window.Clear(Color.White)       _window.Draw(mapSprite)       _window.Draw(charSprite)       _window.Draw(darkSprite)       _window.Display()     End While   End Sub   Private Sub OnClosed(sender As Object, e As EventArgs) Handles _window.Closed     _window.Close()     Application.Exit()   End Sub End Class Link to comment Share on other sites More sharing options...
Damian666 Posted October 4, 2015 Share Posted October 4, 2015 nice tut JC, this is what i got any way to reduce it to increase FPS? Link to comment Share on other sites More sharing options...
Damian666 Posted October 4, 2015 Share Posted October 4, 2015 Sub DrawNight()     'Create a RenderTexture for our Dark/Night overlay     NightGfx.Clear(New SFML.Graphics.Color(0, 0, 0, 200))     For X = TileView.left To TileView.right       For Y = TileView.top To TileView.bottom         If IsValidMapPoint(X, Y) Then           If Map.Tile(X, Y).Type = TILE_TYPE_LIGHT Then             X = ConvertMapX(X * PIC_X) - 135             Y = ConvertMapY(Y * PIC_Y) - 20             'Create the light texture to multiply over the dark texture.             Dim lightSprite As New Sprite(lightGfx)             lightSprite.Position = New SFML.Window.Vector2f(X, Y)             NightGfx.Draw(lightSprite, New RenderStates(BlendMode.Multiply))           End If         End If       Next     Next     GameWindow.Draw(NightSprite)   End Sub this draws on a tile_type_light, but only 1 at a time, how can i make it so it draws more??? DarkDino 1 Link to comment Share on other sites More sharing options...
DarkDino Posted October 4, 2015 Share Posted October 4, 2015 I'm officially a fan of JC, fantastic tutorial. Link to comment Share on other sites More sharing options...
jcsnider Posted October 4, 2015 Author Share Posted October 4, 2015 nice tut JC, this is what i got -snip- any way to reduce it to increase FPS? [*]Make sure you only create the NightTex at start. Do not create it everyframe, just call clear and redraw it. [*]Only update the night tex if something has changed/Player has moved [*]This is complex. Use an off screen texture to store all the map lights when the map loads then every frame render that texture to an empty texture, only draw the player light, and render that. (Less multiplicative drawing.) [*]Accept that lighting is extremely resource hungry and that you should target other areas to optimize. this draws on a tile_type_light, but only 1 at a time, how can i make it so it draws more??? Sub DrawNight() '- snip - Â Â Â Â NightGfx.Display() Â Â Â Â GameWindow.Draw(NightSprite) Â Â End Sub All you need to do (from what I can tell) is call the display function on the Night Texture after you draw all the lights. Link to comment Share on other sites More sharing options...
Damian666 Posted October 4, 2015 Share Posted October 4, 2015 Sub DrawNight()     'Create a RenderTexture for our Dark/Night overlay     NightGfx.Clear(New SFML.Graphics.Color(0, 0, 0, 200))     For X = TileView.left To TileView.right       For Y = TileView.top To TileView.bottom         If IsValidMapPoint(X, Y) Then           If Map.Tile(X, Y).Type = TILE_TYPE_LIGHT Then             X = ConvertMapX(X * PIC_X) - 135             Y = ConvertMapY(Y * PIC_Y) - 20             'Create the light texture to multiply over the dark texture.             Dim lightSprite As New Sprite(lightGfx)             lightSprite.Position = New SFML.Window.Vector2f(X, Y)             NightGfx.Draw(lightSprite, New RenderStates(BlendMode.Multiply))           End If         End If       Next     Next     NightGfx.Display()     GameWindow.Draw(NightSprite)   End Sub still nothing Link to comment Share on other sites More sharing options...
jcsnider Posted October 4, 2015 Author Share Posted October 4, 2015 We figured it out. The problem was that he was using the X and Y variables for his loops and reusing them to calculate coordinates. Here is the fixed code... Sub DrawNight()     'Create a RenderTexture for our Dark/Night overlay     NightGfx.Clear(New SFML.Graphics.Color(0, 0, 0, 200))     For X = TileView.left To TileView.right       For Y = TileView.top To TileView.bottom         If IsValidMapPoint(X, Y) Then           If Map.Tile(X, Y).Type = TILE_TYPE_LIGHT Then             dim X1 = ConvertMapX(X * PIC_X) - 135             dim Y1 = ConvertMapY(Y * PIC_Y) - 20             'Create the light texture to multiply over the dark texture.             Dim lightSprite As New Sprite(lightGfx)             lightSprite.Position = New SFML.Window.Vector2f(X1, Y1)             NightGfx.Draw(lightSprite, New RenderStates(BlendMode.Multiply))           End If         End If       Next     Next     NightGfx.Display()     GameWindow.Draw(NightSprite)   End Sub Link to comment Share on other sites More sharing options...
Damian666 Posted October 4, 2015 Share Posted October 4, 2015 yeah... sorry bout that... >.< Link to comment Share on other sites More sharing options...
Recommended Posts
Create an account or sign in to comment
You need to be a member in order to leave a comment
Create an account
Sign up for a new account in our community. It's easy!
Register a new accountSign in
Already have an account? Sign in here.
Sign In Now