Created
November 19, 2018 02:33
-
-
Save shawn42/786cae191d5fd5ac0ebe40d8cf52405d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$: << '../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