diff --git a/content/pong/scoring/center_line.md b/content/pong/scoring/center_line.md index 1a10fc0..6316e8f 100644 --- a/content/pong/scoring/center_line.md +++ b/content/pong/scoring/center_line.md @@ -8,52 +8,84 @@ Now we need to draw the center line. This will be a fairly basic GeneralRenderer - it doesn't need to react to anything. -```ts -import { GeneralRenderer } from "encompass-ecs"; +In **PongFE/Renderers/CenterLineRenderer.cs**: -export class CenterLineRenderer extends GeneralRenderer { - public layer = 0; +```cs +using System; +using Encompass; +using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; +using PongFE.Components; - private middle: number; - private height: number; +namespace PongFE.Renderers +{ + public class CenterLineRenderer : GeneralRenderer + { + public SpriteBatch SpriteBatch { get; } + public Texture2D WhitePixel { get; } - public initialize(middle: number, height: number) { - this.middle = middle; - this.height = height; - } - - public render() { - love.graphics.setLineWidth(2); - this.dotted_line(this.middle, 0, this.middle, this.height, 10, 10); - } - - private dotted_line(x1: number, y1: number, x2: number, y2: number, dash: number, gap: number) { - const dx = x2 - x1; - const dy = y2 - y1; - const angle = math.atan2(dy, dx); - const st = dash + gap; - const len = math.sqrt(dx * dx + dy * dy); - const nm = (len - dash) / st; - - love.graphics.push(); - - love.graphics.translate(x1, y1); - love.graphics.rotate(angle); - for (let i = 0; i < nm; i++) { - love.graphics.line(i * st + gap * 0.5, 0, i * st + dash + gap * 0.5, 0); + public CenterLineRenderer(SpriteBatch spriteBatch, Texture2D whitePixel) + { + SpriteBatch = spriteBatch; + WhitePixel = whitePixel; } - love.graphics.pop(); + public override void Render() + { + ref readonly var playAreaComponent = ref ReadComponent(); + + DrawDottedLine(playAreaComponent.Width / 2, 0, playAreaComponent.Width / 2, playAreaComponent.Height, 20, 20); + } + + private void DrawDottedLine(float x1, float y1, float x2, float y2, int dash, int gap) + { + var dx = x2 - x1; + var dy = y2 - y1; + var angle = Math.Atan2(dy, dx); + var st = dash + gap; + var len = Math.Sqrt(dx * dx + dy * dy); + var nm = (len - dash) / st; + + SpriteBatch.End(); + SpriteBatch.Begin( + SpriteSortMode.Deferred, + null, + null, + null, + null, + null, + Matrix.CreateRotationZ((float)angle) * Matrix.CreateTranslation(x1, y1, 0) + ); + + for (var i = 0; i < nm; i++) + { + SpriteBatch.Draw( + WhitePixel, + new Rectangle( + (int)(i * st + gap * 0.5), + 0, + dash, + 1 + ), + Color.White + ); + } + + SpriteBatch.End(); + SpriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied); + } } } ``` -I took the dotted line draw procedure from [this helpful forum post](https://love2d.org/forums/viewtopic.php?t=83295) and modified it slightly. Thanks Ref! +I figured out the math for the dotted line procedure so you don't have to. You're welcome. -Add it to the WorldBuilder... +The main magic to understand here is the matrix transformation - the gist of it is that a matrix transformation lets us apply a translation, rotation, and scaling operation all at once and very efficiently. Here we compose a rotation and a translation matrix together so that we can just draw simple rectangles to create the dashed line. Then every SpriteBatch draw rectangle has this transformation applied to it. + +Add our CenterLineRenderer to the WorldBuilder... ```ts -world_builder.add_renderer(CenterLineRenderer).initialize(play_area_width * 0.5, play_area_height); +WorldBuilder.AddRenderer(new CenterLineRendereR()); ``` -![center line](/images/center_line.png) +![center dashed line](/images/center_line.png) diff --git a/content/pong/scoring/display_scaling.md b/content/pong/scoring/display_scaling.md index 01edf78..eafa7a9 100644 --- a/content/pong/scoring/display_scaling.md +++ b/content/pong/scoring/display_scaling.md @@ -125,6 +125,9 @@ Let's revise our **ScoreRenderer** too. } } ``` + +We have introduced a new method above, **ReadComponent**. ReadComponent allows us to read a single arbitrary component of a given type. It is useful when we have a single component of a certain type in the world and we want to just grab data from it. + Now, in our draw method, we will have the Encompass World draw to our GameRenderTarget, and then we will draw that render target using the transform matrix we get from ResolutionScaler. ```cs diff --git a/static/images/center_line.png b/static/images/center_line.png index 4ea361d..93e8cd7 100644 Binary files a/static/images/center_line.png and b/static/images/center_line.png differ