忍者ブログ

マドノマグラの初心者自作ゲーム制作日誌 迷走モード

プログラミング初心者が自作ゲームを作成してみようとする動きを記す。現在迷走中。

ブログ履歴

2010/11/06: HSP3.22の環境を整えてみる
2009/11/09: Rubyで適当版自分用関連記事リンク作成プログラムを作る
2009/06/04: Visual C++(DXライブラリ使用)の環境を整えてみるも休止状態に
2009/03/17: ActionScript勉強の環境を整えるも結局休止状態に
2006/07/15: とりあえず分岐型ゲームを作る グリーンタワーVer1.2 ダウンロード(zip)
2006/07/02: ブログスタート。NScripterで何かを作ろうとする

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

リンゴ回収ゲーム ver1.1 を作成しながらプログラミングを学ぶ

リンゴ回収ゲーム ver1.0 から、Ruby/SDL で始めるゲームプログラミング【後編】に従って、リンゴ回収ゲームを改良。

そこで紹介されていたサンプルパック2をダウンロード後、解凍し、その中に入っていた画像ファイル、サウンドファイル を追加して作成。

rb ファイルに注釈をつけながら勉強したので、元サイトに書かれているコードよりも、このエントリーの続き部分で書いたコードは注釈がやたら多くなっている。それ以外の部分でのコードはほぼ一緒である。

なお、今回、改良したのは、main.rb、items.rb なので、この2つについて続き部分に記述している。


とりあえずゲームらしいゲームはできた、これを応用して色々なゲームが作れたらいいなあ。




main.rb
#! ruby -Ks

# リンゴ回収ゲーム Ver1.1

# ライブラリの読み込み
require "sdl"
require "lib/fpstimer"
require "lib/input"

# 定数の定義 HOLIZON は地平線の高さ
SCREEN_W = 640
SCREEN_H = 480
HOLIZON = 400

# メソッド定義 画像読み込み編
def load_image(fname)

# SDL::Surface.load で画像ファイル読み込み
# ちなみに、SDL::Surface#w で画像の横幅 SDL::Surface#h で画像の縦幅です
image = SDL::Surface.load(fname)

# SDL::Surface#set_color_key(flag,key)
# flagにSDL::SRCCOLORKEY指定で、keyの色が透明化するよ
image.set_color_key(SDL::SRCCOLORKEY, [255, 255, 255])

return image #生成した画像を返す
end

# ライブラリの読み込み player.rbはプレイヤーの挙動 items.rbは落ち物の挙動
require "player"
require "items"

# キー定義 input.rb まかせなので知らん。
class Input
define_key SDL::Key::ESCAPE, :exit
define_key SDL::Key::LEFT, :left
define_key SDL::Key::RIGHT, :right
define_key SDL::Key::RETURN, :ok # Enter キー (タイトル画面で使う)
end


# タイトル画面
class TitleScene
def initialize
@title_image = SDL::Surface.load("image/title.png")
end

def start
end

# Enter が押されたらゲーム画面へ
def act(input)
if input.ok
return :game
else
return nil
end
end

# タイトル画像を表示するだけ
def render(screen)
screen.put(@title_image, 0, 0)
end
end

# ゲーム画面
class GameScene

attr_reader :high_score # @high_score へのアクセサを定義

def initialize(high_score)
@high_score = high_score # 前回のハイスコアを引数から受け取る
@player = Player.new(240)
@items = Items.new

# SDL::Mixer::Music.load で音楽ファイルを読み込み
@bgm = SDL::Mixer::Music.load("sound/famipop3.it")

# フォントの読み込み SDL::TTF.open(filename,size,index=0)
# ファイルから読み出し、そのサイズをsizeで指定
# indexはそのフォントが複数のfaceを持っている場合にどのfaceを使うかを指定
@font = SDL::TTF.open('image/boxfont2.ttf', 50)
end

def start
@items.clear
@score = 0 # ゲームが始まったらスコアを 0 にする

# SDL::Mixer.play_music(読み込んだBGM,再生する回数)で再生。
# 回数に -1 を指定すると無限ループ
SDL::Mixer.play_music(@bgm, -1)
end

# 爆弾に当たったらゲームオーバー画面へ
def act(input)
@player.act(input)
is_crashed,apples = @items.act(@player)

if is_crashed

# ゲームオーバーで BGM 停止。SDL::Mixer.halt_music で音楽停止
SDL::Mixer.halt_music

# ゲームオーバーになったらハイスコアを更新する
@high_score = @score if @high_score < @score
return :game_over
else
@score += apples * 10 # リンゴを 1 個取るごとに 10 点加算する
return nil
end
end

def render(screen)
screen.fill_rect(0, 0, SCREEN_W, HOLIZON, [128, 255, 255])
screen.fill_rect(0, HOLIZON, SCREEN_W, SCREEN_H-HOLIZON, [0, 128, 0])
@player.render(screen)
@items.render(screen)

# スコアを描画する(速い)
# SDL::TTF#draw_solid_utf8(dest,text,x,y,r,g,b)
# dest(Surfaceのインスタンス)に textを (x,y)の所に書きこむ。色はr,g,b
@font.draw_solid_utf8(screen, "score: #{@score} hi-score: #{@high_score}", 0, 0, 255, 0, 0)
end
end

# ゲームオーバー画面
class GameOverScene
def initialize
@game_over_image = SDL::Surface.load("image/game_over.png")
end

def start
@time = 0
end

# 一定時間 (120 フレーム、約 2 秒) 経ったらタイトル画面へ
def act(input)
@time += 1

if @time > 120
return :title
else
return nil
end
end

# ゲームオーバー画像を表示するだけ
def render(screen)
screen.put(@game_over_image, 0, 0)
end
end


class SaveData

def initialize(file_name)
load(file_name)
end
attr_accessor :high_score

# 読み込み
def load(file_name)

# セーブファイルが存在 File.exist?(file_name)=true で読み込み開始
if File.exist?(file_name)

# File.open(path,mode){|file| ...}
# pathで指定されるファイルをオープン、ファイルオブジェクトを返す
# mode r 読み込みモード w 書き込みモード b バイナリモード

# Marshal.load(port)
# port からマーシャルデータを読み込み、
# 元のオブジェクトと同じ状態をもつオブジェクトを生成

data = File.open(file_name, "rb"){|f| Marshal.load(f) }
@high_score = data
else
# セーブファイルが存在しなければ、ハイスコアは 0
@high_score = 0
end
end

# 書き込み
def save
data = @high_score

# Marshal.dump(obj,port)
# obj を再帰的にファイルに書き出し
# port には IO(またはそのサブクラス)のインスタンスを指定
File.open("savefile.dat", "wb"){|f| Marshal.dump(data, f) }
end
end

# SDL の初期化、INIT_EVERYTHING (全て初期化の意味)
SDL.init(SDL::INIT_EVERYTHING)

# SDL::Mixer.open で音の初期化
#frequency=Mixer::DEFAULT_FREQUENCY 周波数
#format=Mixer::DEFAULT_FORMAT サウンドの形式
#cannels=Mixer::DEFAULT_CHANNELS 1でモノラル、2でステレオ
#chunksize=4096 バッファの大きさ(defaultが4096)
# 今回、効果音遅延の可能性を考慮して、バッファの大きさを半分にした
SDL::Mixer.open(SDL::Mixer::DEFAULT_FREQUENCY, SDL::Mixer::DEFAULT_FORMAT,SDL::Mixer::DEFAULT_CHANNELS, 2048)

# フォント機能の初期化
SDL::TTF.init

# SDL.set_video_mode(幅、高さ、bppの値でビデオモードを設定,flags)
# SDL::SWSURFACE システムのメモリ内にバッファをとる。
screen = SDL.set_video_mode(SCREEN_W, SCREEN_H, 16, SDL::SWSURFACE)


# 各シーンのインスタンスを最初に作っておく
save_data = SaveData.new("savefile.dat") #セーブデータの読み込み
Scenes = {
:title => TitleScene.new,
:game => GameScene.new(save_data.high_score), #前回のハイスコアを渡す
:game_over => GameOverScene.new,
}

input = Input.new

#タイマーの生成 fpstimer.rb の FPSTimerLightで、1秒間60フレームに時間調整
timer = FPSTimerLight.new
timer.reset

scene = Scenes[:title] # 最初はタイトル画面から
loop do
input.poll
break if input.exit

next_scene = scene.act(input)
if next_scene # next_scene が nil (か false) でなければ
scene = Scenes[next_scene] # 新しいシーンに移動する
scene.start # シーンが開始したときの処理
end

scene.render(screen)
timer.wait_frame{ screen.update_rect(0, 0, 0, 0) }
end

save_data.high_score = Scenes[:game].high_score
save_data.save

items.rb
#! ruby -Ks

# Itemクラス リンゴと爆弾で共通。これは Items からの呼び出ししかない。
class Item
def initialize(x, y, v)
@x, @y, @v = x, y, v
@is_dead = false
end
attr_reader :v, :image
attr_accessor :x, :y, :is_dead

# 2点間の距離の計算 ピタゴラスの定理から平方根をとっている。
def distance(x1, y1, x2, y2)
Math.sqrt((x1-x2)**2 + (y1-y2)**2)
end

end

# リンゴ
class Apple < Item
def initialize(x, y, v)
super
@image = load_image("image/ringo.bmp")
end

# リンゴの当たり判定
def collides?(player)
px, py = player.center
distance(@x + @image.w / 2, @y + @image.h / 2, px, py) < 56
end

end

# 爆弾
class Bomb < Item
def initialize(x, y, v)
super
@image = load_image("image/bomb.bmp")
end

# 爆弾の当たり判定
def collides?(player)
px, py = player.center
distance(@x + @image.w / 2, @y + @image.h / 2, px, py) < 42
end

end

# Items クラス
class Items
def initialize

# @items は配列っぽい?
@items = [ ]

# SDL::Mixer::Wave.load で効果音読み込み
@sound_get = SDL::Mixer::Wave.load("sound/get.wav")
@sound_bomb = SDL::Mixer::Wave.load("sound/bom08.wav")
end

#一度、ゲームオーバーになった際のリスタートのときにアイテム初期化
def clear
@items = []
end

def act(player)

# 取ったリンゴの数を数える
apples = 0

#爆弾当たったかどうかのフラグ 当たれば true を返して、main.rb でループ脱出
crash = false

#それぞれのアイテムの落下移動
@items.each do |item|
item.y += item.v
item.is_dead = true if item.y > SCREEN_H
end

# 当たり判定 リンゴならリンゴが消えるフラグオン、爆弾ならクラッシュオン
@items.each do |item|
case item
when Apple
if item.collides?(player)
item.is_dead = true

# 効果音再生 SDL::Mixer.play_channel(channel,wave,loops)
# channel : 指定した channe lで wave を演奏。
# -1を指定するとあいているchannelが適当にえらばれる
# loops : 指定した回数繰り返す
# -1のときはendless 0のときは一度のみ演奏
SDL::Mixer.play_channel(-1, @sound_get, 0) # 効果音を鳴らす
apples += 1 # apples を 1 増やす
end
when Bomb
if item.collides?(player)
crash = true
SDL::Mixer.play_channel(-1, @sound_bomb, 0) # 効果音を鳴らす
end
end
end

#画面外に出たもの+プレイヤーと重なったリンゴ 消去
# a.reject!{| item | 条件} 条件真なら、a から item 除去
@items.reject!{|item| item.is_dead}

#新しいアイテムを補充 (つねに画面内に 5 個のアイテムがあるように)
while @items.size < 5
newx = rand(SCREEN_W)
newv = rand(9) + 4
if rand(100) < 60
@items << Bomb.new(newx, 0, newv)
else
@items << Apple.new(newx, 0, newv)
end
end

# 爆弾に当たったかどうかと、取ったリンゴの数を返す
[crash, apples]

end

# 各アイテムの描画準備
def render(screen)
@items.each do |item|
screen.put(item.image, item.x, item.y)
end
end
end
PR

この記事にコメントする

NAME
TITLE
MAIL
URL
COMMENT
PASS

この記事へのトラックバック

この記事にトラックバックする:

PR

カテゴリー

最新記事

アーカイブ

プロフィール

HN:
マドノマグラ
性別:
非公開
職業:
趣味:
駄文書き
自己紹介:
ゲームを作ろうとしているうちに、言語学習に飽きていく。典型的なダメな人パターンに陥るアホな人

リンク

ブログ内検索

バーコード

RSS

忍者ブログ │ [PR]