ユーザ用ツール

サイト用ツール


youtube:crsfml-pong

差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
youtube:crsfml-pong [2024/02/28 18:26] – 削除 - 外部編集 (不明な日付) 127.0.0.1youtube:crsfml-pong [2024/02/29 11:51] (現在) freemikan
行 1: 行 1:
 +====== CrSFML Pong ======
 +
 +{{:youtube:crsfml-pong.png?400|}}
 +
 +
 +===== src/crsfml-pong-take-5.cr =====
 +
 +
 +<file crystal>
 +require "crsfml/graphics"
 +require "./shapeaux"
 +
 +GAME_WIDTH         800
 +GAME_HEIGHT  =       600
 +PADDLE_SPEED = 300.0_f32
 +BALL_SPEED   = 400.0_f32
 +
 +alias ActionMap = Hash(String, SF::Keyboard::Key)
 +
 +class Paddle < SF::Transformable
 +  include SF::Drawable
 +
 +  def initialize(position : SF::Vector2f, size : SF::Vector2f,
 +                 color : SF::Color, speed : Float32, action_map : ActionMap)
 +    super()
 +
 +    @shape = SF::RectangleShape.new(size)
 +    @shape.origin = {size.x / 2, size.y / 2}
 +    @shape.fill_color = color
 +
 +    @speed = speed
 +    @action_map = action_map
 +    @input_direction = SF.vector2f 0, 0
 +
 +    self.position = position
 +  end
 +
 +  def process_input
 +    @input_direction.y = 0
 +    if SF::Keyboard.key_pressed? @action_map["up"]
 +      @input_direction.y = -1.0
 +    end
 +    if SF::Keyboard.key_pressed? @action_map["down"]
 +      @input_direction.y = +1.0
 +    end
 +  end
 +
 +  def update(delta_time : SF::Time)
 +    dy = @input_direction.y * @speed * delta_time.as_seconds
 +    move 0, dy
 +
 +    rect = collision_rect
 +    if top(rect) < 0
 +      self.position = {position.x, @shape.origin.y }
 +    end
 +    if bottom(rect)> GAME_HEIGHT
 +      self.position = {position.x,
 +                       GAME_HEIGHT - rect.height + @shape.origin.y }
 +    end
 +  end
 +
 +  def draw(target : SF::RenderTarget, states : SF::RenderStates)
 +    states.transform *= transform
 +    target.draw @shape, states
 +  end
 +
 +  def collision_rect : SF::FloatRect
 +    SF::FloatRect.new(position - @shape.origin, @shape.size)
 +  end
 +end
 +
 +class Ball < SF::Transformable
 +  include SF::Drawable
 +
 +  def initialize(position : SF::Vector2f, radius : Float32,
 +                  color : SF::Color, velocity : SF::Vector2f)
 +    super()
 +
 +    @shape = SF::CircleShape.new radius
 +    @shape.origin = { radius, radius }
 +    @shape.fill_color = color
 +
 +    @velocity = velocity
 +
 +    self.position = position
 +  end
 +
 +  def update(delta_time : SF::Time, paddles : Array(Paddle))
 +    update_x delta_time, paddles
 +    update_y delta_time, paddles
 +  end
 +
 +  def draw(target : SF::RenderTarget, states : SF::RenderStates)
 +    states.transform *= transform
 +    target.draw @shape, states
 +  end
 +
 +  private def update_x(delta_time : SF::Time, paddles : Array(Paddle))
 +    dx = @velocity.x * delta_time.as_seconds
 +    move dx, 0
 +
 +    paddles.each do |p|
 +      c = collision_circle
 +      r = p.collision_rect
 +
 +      if circle_rect_collide? c, r
 +        if c.center.x > right(r)
 +          self.position = {right(r) + @shape.origin.x, position.y}
 +          @velocity.x *= -1
 +        elsif c.center.x < left(r)
 +          self.position = {left(r) - 2 * c.radius + @shape.origin.x,
 +                           position.y}
 +          @velocity.x *= -1
 +        end
 +      end
 +    end
 +
 +    c = collision_circle
 +    if c.center.x - c.radius < 0
 +      @velocity.x = +@velocity.x.abs
 +    end
 +    if c.center.x + c.radius > GAME_WIDTH
 +      @velocity.x = -@velocity.x.abs
 +    end
 +  end
 +
 +  private def update_y(delta_time : SF::Time, paddles : Array(Paddle))
 +    dy = @velocity.y * delta_time.as_seconds
 +    move 0, dy
 +
 +    paddles.each do |p|
 +      c = collision_circle
 +      r = p.collision_rect
 +
 +      if circle_rect_collide? c, r
 +        if c.center.y < top(r)
 +          self.position = {position.x, top(r) - 2*c.radius + @shape.origin.y}
 +          @velocity.y *= -1
 +        elsif c.center.y > bottom(r)
 +          self.position = {position.x, bottom(r) + @shape.origin.y}
 +          @velocity.y *= -1
 +        end
 +      end
 +    end
 +
 +    c = collision_circle
 +    if c.center.y - c.radius < 0
 +      @velocity.y = +@velocity.y.abs
 +    end
 +    if c.center.y + c.radius > GAME_HEIGHT
 +      @velocity.y = -@velocity.y.abs
 +    end
 +  end
 +
 +  def collision_circle : Circle
 +    x = position.x - @shape.origin.x + @shape.radius
 +    y = position.y - @shape.origin.y + @shape.radius
 +    Circle.new(SF.vector2f(x, y), @shape.radius)
 +  end
 +end
 +
 +def process_events(window : SF::Window)
 +  while event = window.poll_event
 +    if event.is_a? SF::Event::Closed
 +      window.close
 +    end
 +
 +    if event.is_a? SF::Event::KeyPressed
 +      if event.code == SF::Keyboard::Escape
 +        window.close
 +      end
 +    end
 +  end
 +end
 +
 +def setup_paddles : Array(Paddle)
 +  paddles = [] of Paddle
 +  paddles << Paddle.new(
 +    SF.vector2f(40, GAME_HEIGHT / 2),
 +    SF.vector2f(20, 80),
 +    SF::Color::Red,
 +    PADDLE_SPEED,
 +    {"up" => SF::Keyboard::Up, "down" => SF::Keyboard::Down}
 +  )
 +
 +  paddles << Paddle.new(
 +    SF.vector2f(GAME_WIDTH - 40, GAME_HEIGHT / 2),
 +    SF.vector2f(20, 80),
 +    SF::Color::Red,
 +    PADDLE_SPEED,
 +    {"up" => SF::Keyboard::W, "down" => SF::Keyboard::S}
 +  )
 +
 +  paddles
 +end
 +
 +def setupo_balls : Array(Ball)
 +  balls = [] of Ball
 +  n = 100
 +
 +  n.times do |i|
 +    sx = rand(0.5 .. 3.0)
 +    sy = rand(0.5 .. 3.0)
 +    balls << Ball.new(
 +      SF.vector2f(GAME_WIDTH / n * (i + 1), GAME_HEIGHT / 2),
 +      15.0,
 +      SF::Color.new((255 / n * (i + 1)).to_i,
 +                    (255 - (255 / n * (i + 1))).to_i,
 +                    128),
 +      SF.vector2f(BALL_SPEED * sx, BALL_SPEED * sy)
 +    )
 +  end
 +
 +  balls
 +end
 +
 +######################################################################
 +
 +window = SF::RenderWindow.new(
 +  SF::VideoMode.new(GAME_WIDTH, GAME_HEIGHT), "Pong take 5"
 +)
 +
 +paddles = setup_paddles
 +balls = setupo_balls
 +
 +clock = SF::Clock.new
 +elapsed_time = SF::Time::Zero
 +
 +while window.open?
 +  # process events and players' inputs
 +  process_events window
 +  paddles.each { |p| p.process_input }
 +
 +  # update
 +  paddles.each { |p| p.update elapsed_time }
 +  balls.each { |b| b.update elapsed_time, paddles}
 +
 +  # render
 +  window.clear
 +  paddles.each { |p| window.draw p }
 +  balls.each { |b| window.draw b }
 +  window.display
 +
 +  elapsed_time = clock.restart
 +end
 +</file>
 +
 +===== src/shapeaux.cr =====
 +
 +
 +<file crystal>
 +require "crsfml/graphics"
 +
 +def left(r : SF::FloatRect)
 +  r.left
 +end
 +
 +def right(r : SF::FloatRect)
 +  r.left + r.width
 +end
 +
 +def top(r : SF::FloatRect)
 +  r.top
 +end
 +
 +def bottom(r : SF::FloatRect)
 +  r.top + r.height
 +end
 +
 +struct Circle
 +  property center : SF::Vector2f
 +  property radius : Float32
 +
 +  def initialize(@center, @radius)
 +  end
 +end
 +
 +def circle_rect_collide?(circle : Circle, rect : SF::FloatRect) : Bool
 +  c = circle.center
 +
 +  t = c
 +  t.x = left(rect) if c.x < left(rect)
 +  t.x = right(rect) if c.x > right(rect)
 +  t.y = top(rect) if c.y < top(rect)
 +  t.y = bottom(rect) if c.y > bottom(rect)
 +
 +  ct = t - c
 +  distance2 = ct.x ** 2 + ct.y ** 2
 +  radius2 = circle.radius ** 2
 +
 +  distance2 < radius2
 +end
 +</file>
 +
 +===== shard.yml =====
 +
 +
 +<file yaml>
 +name: crsfml-pong-take-5
 +version: 0.1.0
 +
 +authors:
 +  - watercat <watercat@example.com>
 +
 +targets:
 +  crsfml-pong-take-5:
 +    main: src/crsfml-pong-take-5.cr
 +
 +crystal: 1.7.3
 +
 +license: WTFPL
 +
 +dependencies:
 +  crsfml:
 +    github: oprypin/crsfml
 +    version: ~> 2.5.2
 +</file>
  

特に明示されていない限り、本Wikiの内容は次のライセンスに従います: CC0 1.0 Universal
CC0 1.0 Universal Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki