-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Simon! #73
base: main
Are you sure you want to change the base?
Simon! #73
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like it! A lot!
I made a couple changes on the version I'm running here.
This is all 100% optional, but this configuration is what I'm running right now, and the game feels better. There's more distinction between the call and response phases, and the flash between color displays gives the brain a signal that something different is happening.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's your high score!?
Like... 8? 🙈
This is a cool and addictive animation. The question I'm mulling over is if Simon should be an animation, or if it should be an application. I can see pros and cons to each. The animation implementation is like a micro game, and it has limitations to what it can do with only interact
and render
as inputs and outputs. Simon as an application would have access to the entire system; It could be playing sounds along with the animations, tracking a Live View leaderboard, etc.
Not sure I have an answer yet, but this implementation does have novelty.
Also trying to weigh if we want to add another animation before tackling the tech debt of cleaning up the interfaces around animations, config, engine, etc. It's one more file to refactor that has to retain its functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
end | ||
|
||
@impl true | ||
def render(%{state: {:playing_sequence, []}} = state, _config) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't it an %Animation{}
that's passed to render
?
def render(%{state: {:playing_sequence, []}} = state, _config) do | |
def render(%{state: {:playing_sequence, []}} = animation, _config) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, this is also the source of some confusion from your previous code review of the refactor (this is why the callbacks use any
for state, not Animation.t()
. I wanted to avoid passing in the actual animation struct in the refactor to keep things simpler (i.e. the animation implementer knows exactly what they're getting because they defined it themselves.)
This is open for discussion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I might have contributed to that confusion here, sorry! There's actually a field called state
inside the State
struct. So starting from the animation struct it actually looks like animation.state.state
😅
lib/rgb_matrix/animation/simon.ex
Outdated
other_led -> {other_led.id, @black} | ||
end) | ||
|
||
state = %{state | state: {:playing_sequence, rest}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this might have loose type checking unless it's %State{}
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what this means?
We don't currently have any way to achieve this game other than as an animation? There's no support in I'm open to discussion about how to change this, but I had to work within the confines of what's possible right now.
The cost of the future refactor aside, I did this on purpose. The point is to show more possible ways to use the animation behaviour so that a refactor doesn't remove possibilities. This is exactly what happened last time and what caused this entire disrupt in the first place. Because I only originally had 60fps constant math-based animation examples to begin with, the last refactor made it so ONLY that kind animation was possible. I'm trying to come up with as many outlandish animation ideas I can NOW so that a refactor doesn't destroy my ability to do so again in the future. Next up: WPM heatmap! 😝 (kinda kidding, not actually working on this... lol) |
Co-authored-by: Jesse Van Volkinburg <[email protected]>
I think we do support this currently. If the application creates animations for what it wants to do, which is a solid colored pixel for Simon, it can hand those animations to the engine as it wants to play them. Here's some pseudo-code which may help illustrate what I'm trying to describe: defmodule Simon do
use GenServer
# Pseudo-code - Boilerplate omitted...
def init(_) do
state = start_game()
{:ok, state}
end
defp start_game do
%State{
sequence: [] # Sequence of LEDs that light up this round
# ...
}
|> add_to_sequence()
|> play_sequence()
|> start_timer()
end
defp add_to_sequence(state) do
new_sequence_element = %{
location: random_location(),
color: random_color()
}
new_sequence = state.sequence ++ [new_sequence_element]
%State{state | sequence: new_sequence}
end
defp play_sequence(state) do
state.sequence
|> Enum.each(fn element ->
animation = Animation.Implementation.Point.new(
location: element.location,
color: element.color,
duration_in_ms: 500
)
Engine.play_animation(animation, async: false)
end)
end
end |
We no longer support I get your point though. It's just super clunky no? I'd rather Engine expose a painting interface directly, so instead of having to define and play an animation, you just say: "hey engine, paint these pixels these colors". |
Really what I'm getting at in the pseudo-code is that the app has a way to know when to switch to the next animation. There are a handful of options for how we could do that.
Could you expand on this? I'm not quite following. I saw this pseudo-code as pushing Simon "the game" into the application layer and away from modules like Simon "the animation" may appear lighter weight on the surface, but it's limited (or should be limited) in what it can do. It should never overreach the boundary of being an animation, because its render function is in the engine's hot path. It should never do non-animation things, like understand 1 or 2 player mode, play sound effects (if the Keybow had sound), or track/push stats to a leaderboard. As an animation, all it should do is describe the colors of pixels over time. The Simon animation as it's implemented is a great novelty that's fun to play; I think the temptation will be to expand out
I don't have a good feeling about that; it would result in the engine exposing two different paradigms for how to paint pixels. If we need a low-level pixel painting interface that's available in the application layer, I recommend we create an Animation.Implementation.Paint.new(pixels: %{
{0, 0} => "red",
{0, 1} => "yellow",
{1, 0} => "orange",
{1, 1} => "green"
}) I think the more we can keep each module focused on a specific responsibility, the easier the system will be to reason about, as well as maintain. |
Yeah I think all that makes sense. But my original point still stands: the point of this experiment was to show another way to use the animation behaviour, with the specific intent of making sure that any future refactors maintain the ability to build such an animation. Just because we shouldn't use an animation to implement Simon, doesn't mean we shouldn't have the ability to. Does that make sense? |
Also switch back to 500ms for feedback... 850 feels way too long on the real keypad
fd01222
to
2bffa13
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed the bugs introduced by my earlier suggestions.
Added invalid tag; we're going to re-work this as an "application" instead of an animation. |
What's your high score!?