Skip to content

Instantly share code, notes, and snippets.

@shawn42
Created November 19, 2018 02:33
Show Gist options
  • Save shawn42/786cae191d5fd5ac0ebe40d8cf52405d to your computer and use it in GitHub Desktop.
Save shawn42/786cae191d5fd5ac0ebe40d8cf52405d to your computer and use it in GitHub Desktop.
$: << '../lib'
require_relative '../lib/game_ecs'
require 'pry'
require 'gosu'
include Gosu
Q = GameEcs::Query
# Components
class GenerateNewCoinEvent; end
class Input
attr_accessor :left, :right, :up, :down
end
class Tag
attr_accessor :name
def initialize(name)
@name = name
end
end
class Position
attr_accessor :x, :y, :z
def initialize(x,y,z)
@x = x
@y = y
@z = z
end
end
class Size
attr_accessor :width
def initialize(w)
@width = w
end
end
class Velocity
attr_accessor :dx, :dy
def initialize(dx,dy)
@dx = dx
@dy = dy
end
end
class Color
attr_accessor :name
def initialize(name)
@name = name
end
end
class Timer
attr_accessor :ttl, :repeat, :total, :event, :name, :expires_at
def initialize(name, ttl, repeat, event = nil)
@name = name
@total = ttl
@ttl = ttl
@repeat = repeat
@event = event
end
end
class SoundEffectEvent
attr_accessor :sound_to_play
def initialize(sound_to_play)
@sound_to_play = sound_to_play
end
end
class Score
attr_accessor :points
def initialize
@points = 0
end
end
# Systems
class ControlSystem
def update(store, inputs)
downs = inputs[:down_ids]
player_one = store.query(Q.must(Input).must(Tag).with(name: "p1")).first.get(Input)
player_one.left = downs.include?(KbLeft) || downs.include?(GpLeft)
player_one.right = downs.include?(KbRight) || downs.include?(GpRight)
player_one.up = downs.include?(KbUp) || downs.include?(GpUp)
player_one.down = downs.include?(KbDown) || downs.include?(GpDown)
end
end
class MotionSystem
def update(store, inputs)
time_scale = inputs[:dt] * 0.01
accel = 0.03
store.each_entity(Input, Velocity) do |ent|
input, vel = ent.components
vel.dx += accel*time_scale if input.right
vel.dx -= accel*time_scale if input.left
vel.dy += accel*time_scale if input.down
vel.dy -= accel*time_scale if input.up
vel.dx *= 0.95
vel.dy *= 0.95
max = 7
if vec_mag(vel.dx, vel.dy) > max
vel.dx, vel.dy = vec_clip_to_mag(vel.dx, vel.dy, max)
end
end
store.each_entity(Position, Velocity) do |ent|
pos,vel = ent.components
pos.x += vel.dx*time_scale
pos.y += vel.dy*time_scale
pos.x %= 800
pos.y %= 600
end
end
def vec_mag(x,y)
Math.sqrt(x*x + y*y)
end
def vec_clip_to_mag(x,y,mag)
end
end
class SoundSystem
def update(store, inputs)
store.each_entity SoundEffectEvent do |rec|
ent_id = rec.id
effect = rec.get(SoundEffectEvent)
store.remove_component klass: effect.class, id: ent_id
Gosu::Sample.new(effect.sound_to_play).play
end
end
end
class RenderSystem
def initialize
@colors = {
red: Gosu::Color::RED,
green: Gosu::Color::GREEN
}
@font = Gosu::Font.new(40)
end
def update(store,inputs); end
def draw(store, target)
store.each_entity(Position, Score) do |ent|
pos, score = ent.components
@font.draw("#{score.points}", pos.x, pos.y, pos.z, 1.0, 1.0, Gosu::Color::WHITE)
end
store.each_entity(Position, Color, Size) do |ent|
pos, col, size = ent.components
w = size.width
c1 = c2 = c3 = c4 = @colors[col.name]
x1 = pos.x
x2 = pos.x + w
x3 = x2
x4 = x1
y1 = pos.y
y2 = y1
y3 = pos.y + w
y4 = y3
target.draw_quad(x1, y1, c1, x2, y2, c2, x3, y3, c3, x4, y4, c4, pos.z)
end
end
end
class TimerSystem
def update(store, inputs)
delta = inputs[:delta]
current_time_ms = inputs[:total_time]
store.each_entity Timer do |rec|
timer = rec.get(Timer)
ent_id = rec.id
if timer.expires_at
if timer.expires_at < current_time_ms
if timer.event
event_comp = timer.event.is_a?(Class) ? timer.event.new : timer.event
store.add_component component: event_comp, id: ent_id
end
if timer.repeat
timer.expires_at = current_time_ms + timer.total
else
store.remove_component(klass: timer.class, id: ent_id)
end
end
else
timer.expires_at = current_time_ms + timer.total
end
end
end
end
class CoinSystem
def update(store, inputs)
p1_score = store.query(Q.must(Score).must(Tag).with(name: "p1")).first
p1_score, _ = p1_score.components
p1 = store.query(Q.must(Position).must(Size).must(Tag).with(name: "p1")).first
p1_pos, p1_size, _ = p1.components
store.query(Q.must(Position).must(Size).must(Tag).with(name:"coin")).each do |coin|
coin_pos, coin_size, _ = coin.components
if (coin_pos.x >= p1_pos.x &&
coin_pos.x <= p1_pos.x + p1_size.width) ||
(coin_pos.x + coin_size.width >= p1_pos.x &&
coin_pos.x + coin_size.width <= p1_pos.x + p1_size.width)
if (coin_pos.y >= p1_pos.y &&
coin_pos.y <= p1_pos.y + p1_size.width) ||
(coin_pos.y + coin_size.width >= p1_pos.y &&
coin_pos.y + coin_size.width <= p1_pos.y + p1_size.width)
store.remove_entity(id: coin.id)
p1_score.points += 1
store.add_entity SoundEffectEvent.new('coin.wav')
end
end
end
store.each_entity(GenerateNewCoinEvent) do |ent|
Prefab.coin(store, rand(50..750), rand(50..550))
store.remove_component(klass: GenerateNewCoinEvent, id: ent.id)
end
end
end
class Prefab
def self.coin(store, x, y)
store.add_entity(Position.new(x,y,1), Velocity.new(0.5-rand,0.5-rand), Color.new(:green), Size.new(10), Tag.new("coin") )
end
def self.player(store, x, y)
store.add_entity(Position.new(400,40,3), Tag.new("p1"), Score.new)
store.add_entity(Position.new(x,y,2), Velocity.new(0,0), Color.new(:red), Tag.new("p1"), Input.new, Size.new(20))
end
def self.coin_gen_timer(store)
store.add_entity(Timer.new(:coin_gen, 2000, true, GenerateNewCoinEvent))
end
end
class CoinGetterGame < Window
def initialize
super(800,600)
@entity_store = GameEcs::EntityStore.new
@render_system = RenderSystem.new
@downs = []
@systems = [
ControlSystem.new,
MotionSystem.new,
TimerSystem.new,
CoinSystem.new,
SoundSystem.new,
@render_system
]
Prefab.player(@entity_store, 400, 300)
10.times do
Prefab.coin(@entity_store, rand(50..750), rand(50..550))
end
Prefab.coin_gen_timer(@entity_store)
end
def button_down(id)
close if id == KbEscape
@downs << id
end
def button_up(id)
@downs.delete id
end
def update
@systems.each do |sys|
sys.update(@entity_store, {dt: relative_delta, down_ids: @downs, total_time: Gosu::milliseconds})
end
end
def draw
@render_system.draw(@entity_store, self)
end
private
MAX_UPDATE_SIZE_IN_MILLIS = 500
def relative_delta
total_millis = Gosu::milliseconds.to_f
@last_millis ||= total_millis
delta = total_millis
delta -= @last_millis if total_millis > @last_millis
@last_millis = total_millis
delta = MAX_UPDATE_SIZE_IN_MILLIS if delta > MAX_UPDATE_SIZE_IN_MILLIS
delta
end
end
if $0 == __FILE__
game = CoinGetterGame.new
game.show
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment