encompass-cs-docs/content/pong/move_paddle/magic_values.md

3.4 KiB

title date weight
Magic Values 2019-05-23T15:51:58-07:00 25

Our code right now is violating one more good architecture principle.

    ...

    if (keyboardState.IsKeyDown(Keys.Down))
    {
        SendMessage(
            new MotionMessage(playerInputEntity,
            new System.Numerics.Vector2(0, 10 * (float)dt))
        );
    }

    ...

Magic values refer to numbers that have been placed directly in the code. That 10 above is a magic value. Why are magic values bad?

Magic values introduce the possibility of duplication. Let's say I start adding my code to be able to move paddles down. Now I have to change the numbers in two different places. If I ever change one without changing the other, I have introduced a bug.

There's another reason to avoid magic values too. Suppose I haven't looked at the InputEngine for a while, but I suddenly decide that the paddles are moving too slow. Intuitively, I would want to look for a Component that contains those values, but instead, they would be hidden in the InputEngine. This isn't what you would really expect - why should Input have anything to do with the speed of the paddles?

Organizing your information consistently is crucial to being able to easily find things that you need to change. You'll thank yourself later.

Let's make a new Component.

Create a file: PongFE/Components/PaddleMoveSpeedComponent.cs

using Encompass;

namespace PongFE.Components
{
    public struct PaddleMoveSpeedComponent : IComponent
    {
        public float Speed { get; }

        public PaddleMoveSpeedComponent(float speed)
        {
            Speed = speed;
        }
    }
}

And let's add it to our paddle Entity.

In PongFEGame.cs

    ...

    WorldBuilder.SetComponent(paddle, new PlayerInputComponent(PongFE.Components.PlayerIndex.One));
    WorldBuilder.SetComponent(paddle, new PaddleMoveSpeedComponent(10));
    WorldBuilder.SetComponent(paddle, new PositionComponent(new MoonTools.Structs.Position2D(5, 5)));

    ...

Now let's tell our InputEngine to use it, and why don't we go ahead and make the Up key move the paddle upward too.

    ...

    foreach (ref readonly var playerInputEntity in ReadEntities<PlayerInputComponent>())
    {
        ref readonly var playerInputComponent = ref GetComponent<PlayerInputComponent>(playerInputEntity);

        if (HasComponent<PaddleMoveSpeedComponent>(playerInputEntity))
        {
            ref readonly var paddleMoveSpeedComponent = ref GetComponent<PaddleMoveSpeedComponent>(playerInputEntity);
            var paddleSpeed = paddleMoveSpeedComponent.Speed;

            if (playerInputComponent.PlayerIndex == PlayerIndex.One)
            {
                if (keyboardState.IsKeyDown(Keys.Down))
                {
                    SendMessage(
                        new MotionMessage(
                            playerInputEntity,
                            new System.Numerics.Vector2(0, paddleSpeed * (float)dt)
                        )
                    );
                }
                else if (keyboardState.IsKeyDown(Keys.Up))
                {
                    SendMessage(
                        new MotionMessage(
                            playerInputEntity,
                            new System.Numerics.Vector2(0, -paddleSpeed * (float)dt)
                        )
                    );
                }
            }
        }
    }

    ...

I'm starting to get get a bad feeling about this. Let me explain.