Last active
December 17, 2015 17:19
簡単なジャンプアクションゲーム
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
# -*- coding: utf-8 -*- | |
%w[xot beeps rays reflex] | |
.map {|s| File.expand_path "../../../../#{s}/lib", __FILE__} | |
.each {|s| $:.unshift s if !$:.include?(s) && File.directory?(s)} | |
require 'reflexion/include' | |
$player = | |
$ground = nil | |
$score = 0 | |
$gameover = false | |
$left = | |
$right = false | |
class View | |
def remove_self () | |
parent.remove self if parent | |
end | |
alias center_noangle center | |
def center | |
p, c, a = pos, center_noangle, angle | |
a == 0 ? c : p + (c - p).rotate(a) | |
end | |
end | |
class KeyEvent | |
def left? () | |
code == 123 | |
end | |
def right? () | |
code == 124 | |
end | |
def space? () | |
code == 49 | |
end | |
end | |
setup do | |
size 800, 400 # ウィンドウサイズの調整 | |
flow :none # (おまじない) | |
gravity 0, 9.8 * meter # 画面下方向の重力 | |
root.wall.clear_fixtures # 標準でウィンドウの枠として壁が作られてしまうので除去 | |
add_ground # 地面要素の追加 | |
add_bricks 50 # 障害物を 50 個追加 | |
add_coins 50 # スコアを稼ぐ用のコインを 50 個追加 | |
add_enemies 10 # 敵キャラを 10 体追加 | |
add_player # プレイヤーキャラクタの追加 | |
end | |
def add_bricks (count) | |
range = place_range | |
count.times do | |
window.add RectShape.new { # 矩形の障害物をウィンドウに追加 | |
pos rand(range), 200 # X の位置はランダム、Y の位置は上から 200 ピクセルの位置 | |
size *(0..1).map {rand(20..100)} # 縦・横の大きさはそれぞれ 20〜100 のランダム | |
fill rand, rand, rand # 色も RGB それぞれランダム | |
dynamic true # 物理演算の対象であり固定されない形状 | |
density 1 # 密度指定をしないと形状が回転しない | |
} | |
end | |
end | |
def add_coins (count) | |
range = place_range | |
count.times do | |
window.add EllipseShape.new { # 丸のコインオブジェクトをウィンドウに追加 | |
pos rand(range), 100 # X の位置はランダム、Y の位置は障害物よりも上となるように 100 ピクセルの位置 | |
size 30 # 縦横は 30 で固定 | |
fill :yellow # コインらしく黄色に | |
static true # 物理演算の対象にするが、コイン自体は位置固定のため static 指定 | |
sensor true # 幽霊のように他形状と重なっても衝突しない | |
on :contact do # 他の形状と接触したときの処理 | |
remove_self # 自身を削除 | |
$score += 1 # スコアに +1 | |
end | |
} | |
end | |
end | |
def add_enemies (count) | |
range = place_range | |
count.times do | |
window.add RectShape.new { # 矩形をウィンドウに追加 | |
pos rand(range), 200 # X の位置はランダム、Y の位置はコインの少し下になるように | |
size 50 # 縦横の大きさは 50 固定 | |
fill :red # 敵は危険なので赤に | |
static true # 衝突の処理はしたいので物理演算対象にするが位置は動かさないので static 指定 | |
sensor true # 衝突の検出のみで物体同士で反発はしない | |
on :contact do |e| # 他の物体と接触した時の処理 | |
$gameover = true if e.view == $player | |
# 接触相手がプレイヤーだったらゲームオーバーフラグを立てる | |
end | |
} | |
end | |
end | |
def place_range () | |
$ground.frame.inset_by(100).tap {|f| break f.left .. f.right} | |
end | |
def add_ground () | |
$ground = window.add View.new { # 衝突判定用の形状の指定と描画は自分でやるのでただの View をウィンドウに追加 | |
width 10000 # 横スクロールのゲームなので横幅を長く | |
height parent.height # 縦幅はウィンドウの高さに合わせる | |
static true # 物理演算の対象に | |
w, h = width, height | |
edges = (0..w).step(5).map do |x| # 0〜10000ピクセルの間を5ピクセル間隔で線分を追加していく | |
noise = Rays.perlin(x / 100.0, 0) # Perlin ノイズを使ってデコボコに | |
[x, h + noise * 30 - 50] # noise は -1.0〜1.0 なので 30 倍することで -30.0〜30.0 に | |
end | |
edges = [[0, 0]] + edges + [[w, 0]] # 地面だけだと左右の端で落ちてしまうので壁も追加 | |
body.clear_fixtures | |
body.add_edge *edges # 生成した点の配列を物理演算用の線形状として登録 | |
on :draw do |e| # 地面の描画処理 | |
e.painter.push fill: nil, stroke: :white do # ブロック内は塗り無し・白い線で描画 | |
lines *edges # 線を描画 | |
end | |
end | |
} | |
end | |
def add_player () | |
$player = window.add EllipseShape.new { | |
# プレイヤーは丸い形状 | |
@jump_count = 0 # 多段ジャンプの判定用 | |
pos 50, 50 # 初期位置 | |
size 30 # 幅と高さは 30 | |
dynamic true # 重力にしたがって落下するので dynamic 指定 | |
density 1 # 密度 | |
friction 1 # 摩擦 | |
def self.jumpable? () # ジャンプ可能な状態かを返す | |
@jump_count <= 1 # 2段ジャンプまで | |
end | |
def self.jump () # ジャンプする | |
return unless jumpable? # ジャンプ不可の状況なら何もしない | |
v = velocity # その時点での速度のベクトルを取得 | |
v.y = -5 * meter # Y 方向の速度を 5 メートルに | |
velocity v # 上向きの速度を設定することでジャンプさせる | |
@jump_count += 1 # 多段ジャンプ 1 回消費 | |
end | |
on :update do # 毎フレームのプレイヤー形状の更新処理 | |
dir = 0 | |
dir -= 1 if $left # $left, $right は Boolean | |
dir += 1 if $right # ユーザの入力状況に応じて挙動を変える | |
self.angular_velocity = 360 * 3 * dir | |
# 1 秒当たり 3 回転させる | |
end | |
on :contact_begin do # 他形状に接触したら? | |
@jump_count = 0 # ジャンプのカウントをクリア | |
end | |
} | |
end | |
update do # 毎フレームごとに呼ばれる処理 | |
old_x = window.root.scroll.x # 現在の横スクロール位置 | |
new_x = $player.center.x - window.width / 2 # プレイヤーの位置 | |
window.root.scroll_to (old_x + new_x) / 2, 0 # 上 2 つの位置を元に、プライヤーを追従するように画面をスクロールさせる | |
end | |
draw do # 毎フレームの描画処理 | |
fill :white # 白塗りで | |
font nil, 30 # 大きさ 30 の標準フォントで | |
text "SCORE: #{$score}", 10, 10 # スコアを左上に描画 | |
text "#{event.fps.to_i} FPS", 10, 50 # その下に FPS も描画 | |
if $gameover # ゲームオーバーフラグが立ってたら | |
fill :red # 赤塗りで | |
font nil, 100 # 大きさ 100 の標準フォントで | |
text "GAMEOVER!", 100, 100 # ゲームオーバーを表示 | |
end | |
end | |
key do # キー入力ごとに呼ばれる処理 | |
next unless down? || up? # キーの押下もしくは押上の時のみ処理したい | |
$left = down? if left? # 左キーが押下された? | |
$right = down? if right? # 右キーが押下された? | |
$player.jump if space? && down? # スペースキーの押下だったらプライヤーをジャンプ | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment