Imports System
Imports System.IO
Imports y4cs.ytl
Imports y4cs.aux
Imports y4cs.timer
Imports y4cs.math
Imports y4cs.draw
Imports y4cs.sound
Imports y4cs.input
Namespace Sample7
Interface IPlay
Function time() As Integer
Function isPress(ByVal k As Integer) As Boolean
Function update() As Boolean
Sub close()
End Interface
Class Play
Implements IPlay
Private key As Key2
Private timer As FixTimer
Private bw As BinaryWriter
Public Sub New()
key = New Key2
bw = New BinaryWriter(File.Create("replay.dat"))
End Sub
Public Function time() As Integer Implements IPlay.time '
Return timer.get()
End Function
Public Function isPress(ByVal k As Integer) As Boolean Implements IPlay.isPress
Return key.isPress(k)
End Function
Public Function update() As Boolean Implements IPlay.update
If timer Is Nothing Then
timer = New FixTimer
timer.reset()
Else
timer.update()
End If
key.update()
bw.Write(timer.get())
Dim state As Byte = 0
Dim i As Integer
i = 0
While i <= 5
If key.isPress(i) Then
state = state Or CByte(1 << i)
End If
i += 1
End While
bw.Write(state)
Return True
End Function
Public Sub close() Implements IPlay.close
If Not (bw Is Nothing) Then
bw.Close()
bw = Nothing
End If
End Sub
End Class
Class Replay
Implements IPlay
Private br As BinaryReader
Private key As Byte
Private tm As Integer
Public Sub New(ByVal filename As String)
br = New BinaryReader(File.Open(filename, FileMode.Open))
key = 0
tm = 0
End Sub
Public Function time() As Integer Implements IPlay.time
Return tm
End Function
Public Function isPress(ByVal k As Integer) As Boolean Implements IPlay.isPress
Return (key And (1 << k)) <> 0
End Function
Public Function update() As Boolean Implements IPlay.update
If br.PeekChar() = -1 Then
Return False
End If
tm = br.ReadInt32()
key = br.ReadByte()
Return True
End Function
Public Sub close() Implements IPlay.close
If Not (br Is Nothing) Then
br.Close()
br = Nothing
End If
End Sub
End Class
'/ <summary>
'/ App の概要の説明です。
'/ </summary>
Class App
Private Shared rand As rand
Private Shared level As Integer ' ゲームレベル
Structure Tama
Public x, y As Single ' 座標
Public vx, vy As Single ' 速度ベクトル
Public speed As Single ' 移動速度(vx,vyはこれで正規化される)
Public ax, ay As Single ' 加速度ベクトル
Public spin As Single ' 追尾度
Public alive As Boolean ' この弾の生存フラグ
Public r, g, b As Integer
Public Sub reset()
alive = False
End Sub
Public Sub setColor(ByVal r_ As Integer, ByVal g_ As Integer, ByVal b_ As Integer)
r = r_
g = g_
b = b_
End Sub
Public Sub move(ByVal x_ As Single, ByVal y_ As Single)
If Not alive Then
Return
End If ' vx,vyを正規化する
Dim v_dist As Single = CSng(Math.Sqrt(vx * vx + vy * vy))
If v_dist < 0.1 Then
Return ' 正規化できナー
End If
vx = (vx / v_dist) * speed
vy = (vy / v_dist) * speed
x += vx
y += vy
vx += ax
vy += ay
Dim ax_ As Single = x_ - x
Dim ay_ As Single = y_ - y
' ax_,ay_を正規化して、spinを掛ける
Dim a_dist As Single = CSng(Math.Sqrt(ax_ * ax_ + ay_ * ay_))
ax_ = (ax_ / a_dist) * spin
ay_ = (ay_ / a_dist) * spin
ax += ax_
ay += ay_
' 画面範囲外に消えたら死亡(ただし、画面外のト音記号の発射した♪が
' 到達できるように配慮する
If x < -100 Or y < -200 Or x > (640 + 100) Or y > (480 + 200) Then
alive = False
End If
End Sub
Public Sub reflect(ByVal x_ As Single, ByVal y_ As Single)
' (x_,y_)と反対方向にふっとぶ
vx = x - x_
vy = y - y_
Dim v_dist As Single = CSng(Math.Sqrt(vx * vx + vy * vy))
If v_dist < 0.1 Then
Return ' 正規化できナー
End If
vx = vx / v_dist
vy = vy / v_dist
speed = speed * 4
End Sub
Public Sub onDraw(ByVal dst As Screen, ByVal src As Texture)
If Not alive Then
Return
End If
dst.setColor(r, g, b)
dst.blt(src, CInt(x) - 16, CInt(y) - 20)
End Sub
' 判定位置は、ハートのLOVEのOとVとの間と、
' ♪のおたまじゃくしのところ
Public Sub set_(ByVal x_ As Single, ByVal y_ As Single, ByVal vx_ As Single, ByVal vy_ As Single, ByVal ax_ As Single, ByVal ay_ As Single, ByVal spin_ As Single, ByVal speed_ As Single)
x = x_
y = y_
vx = vx_
vy = vy_
ax = ax_
ay = ay_
spin = spin_
speed = speed_
setColor(CInt(rand.get(128)), CInt(rand.get(128)), CInt(rand.get(128)))
alive = True
End Sub
End Structure
Private Shared tama_max As Integer = 256
Private Shared speed As Single = 4
' 弾のスピード
Shared Function search(ByVal tama() As Tama) As Integer
' 空き番を返す。なければ-1。
Dim i As Integer
i = 0
While i < tama_max
If Not tama(i).alive Then
Return i
End If
i += 1
End While
Return -1
End Function
Shared Sub shot(ByVal tama() As Tama, ByVal from_x As Single, ByVal from_y As Single, ByVal to_x As Single, ByVal to_y As Single, ByVal type As Integer)
Dim n As Integer
n = search(tama)
If n = -1 Then
Return
End If
Dim vx As Single = to_x - from_x
Dim vy As Single = to_y - from_y
' vx,vyを正規化
Dim dist As Single = CSng(Math.Sqrt(vx * vx + vy * vy))
If dist < 0.1 Then
Return
End If
vx /= dist
vy /= dist
vx *= speed
vy *= speed
Dim way As Integer = 0
Dim step_ As Integer
step_ = 0
Select Case type
Case 1 ' level way 方向固定
step_ = 512 / level
way = step_
Dim i As Integer
i = 0
While i < level
tama(n).set_(from_x, from_y, SinTable.get().cos(way), SinTable.get().sin(way), 0, 0, 0, speed)
n = search(tama)
If n = -1 Then
Return
End If
way += step_
i += 1
End While
Case 2 ' 誘導 5way
Try
way = SinTable.get().atan(CInt(vx), CInt(vy)) >> 7 ' 65536/512
Catch
End Try
tama(n).set_(from_x, from_y, SinTable.get().cos((way + 0)), SinTable.get().sin((way + 0)), 0, 0, 0.005F, speed)
n = search(tama)
If n = -1 Then
Return
End If
tama(n).set_(from_x, from_y, SinTable.get().cos((way + 30)), SinTable.get().sin((way + 50)), 0, 0, 0.005F, speed)
n = search(tama)
If n = -1 Then
Return
End If
tama(n).set_(from_x, from_y, SinTable.get().cos((way - 30)), SinTable.get().sin((way - 50)), 0, 0, 0.005F, speed)
If level <= 25 Then
Return ' level 25以下なら3way
End If
n = search(tama)
If n = -1 Then
Return
End If
tama(n).set_(from_x, from_y, SinTable.get().cos((way + 60)), SinTable.get().sin((way + 100)), 0, 0, 0.005F, speed)
n = search(tama)
If n = -1 Then
Return
End If
tama(n).set_(from_x, from_y, SinTable.get().cos((way - 60)), SinTable.get().sin((way - 100)), 0, 0, 0.005F, speed)
Case 0 ' 5way
Try
way = SinTable.get().atan(CInt(vx), CInt(vy)) >> 7 ' 65536/512
Catch
End Try
tama(n).set_(from_x, from_y, SinTable.get().cos((way + 0)), SinTable.get().sin((way + 0)), 0, 0, 0, speed)
n = search(tama)
If n = -1 Then
Return
End If
tama(n).set_(from_x, from_y, SinTable.get().cos((way + 30)), SinTable.get().sin((way + 30)), 0, 0, 0, speed)
n = search(tama)
If n = -1 Then
Return
End If
tama(n).set_(from_x, from_y, SinTable.get().cos((way - 30)), SinTable.get().sin((way - 30)), 0, 0, 0, speed)
If level <= 15 Then
Return ' level 15以下なら3way
End If
n = search(tama)
If n = -1 Then
Return
End If
tama(n).set_(from_x, from_y, SinTable.get().cos((way + 60)), SinTable.get().sin((way + 60)), 0, 0, 0, speed)
n = search(tama)
If n = -1 Then
Return
End If
tama(n).set_(from_x, from_y, SinTable.get().cos((way - 60)), SinTable.get().sin((way - 60)), 0, 0, 0, speed)
Case 3 ' 32 way 方向固定
Dim ways As Integer = 32
If level < 20 Then
ways /= 2
End If
If level < 10 Then
ways /= 2
End If
step_ = 512 / ways
way = step_
Dim i As Integer
i = 0
While i < ways
tama(n).set_(from_x, from_y, SinTable.get().cos(way), SinTable.get().sin(way), 0, 0, 0, speed)
n = search(tama)
If n = -1 Then
Return
End If
way += step_
i += 1
End While
End Select
End Sub
Shared Function isCross(ByVal x1 As Single, ByVal y1 As Single, ByVal x2 As Single, ByVal y2 As Single, ByVal w As Single, ByVal h As Single) As Boolean
' (x1,y1)を左上とする、幅w,高さhの矩形に(x2,y2)が含まれればtrueを返す
Return (x1 < x2) And (x2 < x1 + w) And (y1 < y2) And (y2 < y1 + h)
End Function
'/ <summary>
'/ アプリケーションのメイン エントリ ポイントです。
'/ </summary>
Overloads Shared Function Main(ByVal args() As String) As Integer
Try
Dim screen As New Screen
screen.setCaption("♪ゲー")
Dim play As IPlay
screen.beginScreenTest() ' いまからスクリーンテストをする
screen.testVideoMode(640, 480, 0) ' ウィンドゥモード 640×480をテスト
screen.endScreenTest() ' テスト終了
Dim time As New FixTimer
rand = New Rand
Dim key As New Key2
Dim mouse As New MouseInput
mouse.hide() ' マウスカーソル消しておく
' Texture texture = new Texture;
' texture.load("Star.gif");
Dim heart As New Texture
heart.load("heart.png")
Dim onpu As New Texture
onpu.setColorKeyPos(0, 0)
onpu.load("onpu.gif")
Dim touon As New Texture
touon.load("touon.png")
' 20fps * 60sec * 5
Dim timing_data() As Byte ' [20*60*5]
timing_data = CType(FileSys.read("timingdata.bin"), Byte())
Dim fpsTimer As New FpsTimer
fpsTimer.setFps(60)
Dim bgm As New Sound
bgm.load("nyokiMIX_de44.ogg", 0)
Dim tx(), ty() As Single ' ト音記号ちゃん
tx = New Single(3) {}
ty = New Single(3) {}
Dim ton(3) As Integer ' ト音記号ちゃんの♪発射後のフェードレート
Dim x, y As Single ' 自機の位置(ハート)
Dim tama(tama_max - 1) As Tama
Dim last_scan As Integer ' 前回、弾発射を検査した位置
Dim score As Integer ' スコア
Dim myleft As Integer ' 残り機数
Dim highscore As Integer = 0 ' ハイスコア
Dim gameover As Boolean
Dim deadtime As Integer ' 死亡後の処理時間
Dim charge As Integer ' スペースキーでの貯め
Dim bx As Integer = 0
Dim by As Integer = 0
Dim br As Integer = 0
Dim bc As Integer = 0
Dim bn As Integer = 0 ' 倍率の表示
Dim extend As Integer ' 自機のextend
Dim charged As Boolean ' チャージ中か
replay:
If args.Length = 0 Then
play = New Play
Else
play = New Replay(args(0))
End If
charged = False
extend = 500000
bc = 0
bn = 0
charge = 0
deadtime = 0
gameover = False
myleft = 3
score = 0
last_scan = 0
x = 320
y = 240
tx(0) = -touon.getWidth()
ty(0) = 0
tx(1) = 640
ty(1) = 480 - touon.getHeight()
tx(2) = 0
ty(2) = 480
tx(3) = 640 - 100
ty(3) = -touon.getHeight()
ton(0) = 0
ton(1) = 0
ton(2) = 0
ton(3) = 0
For i As Integer = 0 To tama.Length - 1
tama(i).reset()
Next
level = 0
bgm.play()
While GameFrame.pollEvent() = 0
' isPress(0)だけ特別扱い
key.update()
If key.isPress(0) Then
Exit While
End If
If gameover Then
' enter key で replay
play.close()
If key.isPush(6) Then
GoTo replay
End If
GoTo moveSkip
End If
play.update()
level = 1 + CInt(play.time() / 10000)
If (True) Then
Dim dist As Single = 4.0F ' 一回の移動可能距離
Dim vx As Single
Dim vy As Single
vx = 0
vy = 0
If play.isPress(1) Then
vy = -dist
End If
If play.isPress(2) Then
vy = +dist
End If
If play.isPress(3) Then
vx = -dist
End If
If play.isPress(4) Then
vx = +dist
End If
If vx <> 0 And vy <> 0 Then
vx *= 0.7F
vy *= 0.7F ' 斜め移動のペナルティ
End If
If play.isPress(5) Then
charge = charge + 1
charged = True
Else
' chargeの量に応じて、弾を反射
If charge <> 0 Then
Dim num As Integer
num = (charge + 59) / 60
If num >= 2 Then
Dim d As Single
If num > 20 Then
d = 1 << 25
Else
d = 1 << (num + 6)
End If
bn = 0
For i As Integer = 0 To tama.Length - 1
If Not tama(i).alive Then
GoTo ContinueWhile2
End If
If isCross(tama(i).x - (d / 2), tama(i).y - (d / 2), x, y, d, d) Then
tama(i).reflect(x, y) ' 跳ね返す
score += (200 * num)
bn += 1
End If
ContinueWhile2:
Next
bx = CInt(x)
by = CInt(y)
br = num
bc = 60
End If
charge = 0
charged = False
Else
End If ' no chargeなのでscoreをtimeに比例して加算
End If ' score += ft/100;
If charge = 0 Then
vx *= 2
vy *= 2
End If
x += vx
y += vy
If x < 0 Then
x = 0
End If
If x > 640 Then
x = 640
End If
If y < 0 Then
y = 0
End If
If y > 480 Then
y = 480
End If
End If
' ト音記号の移動
tx(0) += 2
If tx(0) > 640 Then
tx(0) = -touon.getWidth()
End If
tx(1) -= 2
If tx(1) < (0 - touon.getWidth()) Then
tx(1) = 640
End If
ty(2) += 2
If ty(2) > 480 Then
ty(2) = -touon.getHeight()
End If
ty(3) -= 2
If ty(3) < (0 - touon.getHeight()) Then
ty(3) = 480
End If
For i As Integer = 0 To tama.Length - 1
tama(i).move(x, y)
Next
' 弾発生
Dim t As Integer
t = CInt(play.time() * 0.02)
For i As Integer = 0 To 3
ton(i) = 0
Next
' フレームスキップを考慮して、発射されるはずであった弾をサーチ
While last_scan < t
' ひとつ前で押されていなくて、今回押されているの意味
Dim d As Byte
d = CByte(timing_data(last_scan + 1) And (Not timing_data(last_scan)))
Dim w As Integer
w = (CInt(touon.getWidth()) / 2)
Dim h As Integer
h = CInt(touon.getHeight()) / 2
If (d And 1) <> 0 Then 'key.isPress(5)) {
shot(tama, tx(0) + w, ty(0) + h, x + 50, y + 40, 0)
End If
If (d And 2) <> 0 Then 'key.isPress(6)) {
shot(tama, tx(1) + w, ty(1) + h, x + 50, y + 40, 1)
End If
If (d And 4) <> 0 Then ' key.isPress(7)) {
shot(tama, tx(2) + w, ty(2) + h, x + 50, y + 40, 2)
End If
If (d And 8) <> 0 Then ' key.isPress(8)) {
shot(tama, tx(3) + w, ty(3) + h, x + 50, y + 40, 3)
End If
' ♪発射フェーズでは、ト音記号を暗くする
Dim d2 As Byte
d2 = timing_data(last_scan)
If (d And 1) <> 0 Then
ton(0) = 128
End If
If (d And 2) <> 0 Then
ton(1) = 128
End If
If (d And 4) <> 0 Then
ton(2) = 128
End If
If (d And 8) <> 0 Then
ton(3) = 128
End If
last_scan += 1
End While
score += 10
If (True) Then
'
Dim w As Integer = CInt(touon.getWidth())
Dim h As Integer = CInt(touon.getHeight())
For i As Integer = 0 To 3
If isCross(tx(i), ty(i), x, y, w, h) Then
' score += 1000; // もりもりアップ!
If charged Then
charge += 2 ' チャージ中なら高速チャージ
End If
End If
Next
End If
' 当たり判定
If deadtime = 0 Then
Const d As Single = 10.0F ' 判定矩形サイズ
For i As Integer = 0 To tama.Length - 1
If Not tama(i).alive Then
GoTo ContinueWhile3
End If
If isCross(tama(i).x - d / 2, tama(i).y - d / 2, x, y, d, d) Then
deadtime = 100 ' 死亡カウント
End If
ContinueWhile3:
Next
Else
deadtime -= 1
If deadtime = 0 Then
' 死亡
myleft -= 1
charge = 200 ' 復活サービス
End If
End If
If myleft < 0 Then
gameover = True
bgm.stop()
End If
If bc <> 0 Then
bc = bc - 1
End If
' ----- ↑↑ 遅いマシンでも移動処理だけは保証する
moveSkip:
fpsTimer.waitFrame()
If fpsTimer.toBeSkip() Then
GoTo ContinueWhile1 ' 描画キャンセル
End If
screen.setClearColor(255, 255, 255)
screen.clear()
screen.blendSrcAlpha()
' screen.disableBlend();
' 自機のextend (every 500000)
If score > extend Then
myleft += 1
extend += 500000
End If
' 残機
Dim hs As New Size
Dim r As New Rect
hs.setSize(CInt(heart.getWidth()) / 2, CInt(heart.getHeight()) / 2)
screen.setColor(255, 255, 255)
For i As Integer = 0 To myleft - 1
screen.blt(heart, CInt(i * heart.getWidth() / 2), 50, r, hs)
Next
' ト音記号の描画
Dim faderate As Single = CSng(SinTable.get().cos0_1(CInt(play.time() / 4)))
Dim fade127 As Single = faderate * 127
If (True) Then
Dim i As Integer
i = 0
While i < 4
Dim tn As Integer
tn = ton(i)
screen.blendSrcAlpha()
Select Case i
Case 0
GoTo Case1
Case 1
Case1:
screen.setColor(CInt(128 + fade127 - tn), CInt(128 + fade127 - tn), CInt(128 + fade127 - tn))
Case 2
screen.setColor(CInt(128 + fade127 - tn), 0, 0)
Case 3
screen.setColor(0, 0, CInt(128 + fade127 - tn))
End Select
screen.blt(touon, CInt(tx(i)), CInt(ty(i)))
i += 1
End While
End If
' 自機
If Not gameover Then
screen.setColor(255, 255, 255)
If deadtime = 0 Then
screen.blt(heart, CInt(x) - 50, CInt(y) - 40) ' ハートの中心が指定座標となるように
If charge <> 0 Then
Dim s As New Size
Dim dmy As New Rect
s.setSize(CInt(heart.getWidth() / 4), CInt(heart.getHeight() / 4))
Dim offset As Integer = CInt(play.time()) / 2 ' 小さなハートくるくるまわす
Dim num As Integer = (charge + 59) / 60 ' 小さなハートの数
If num > 10 Then
num = 10
End If
If (True) Then
Dim i As Integer
i = 0
While i < num And i < 10
Dim xx As Single = SinTable.get().cos((i * 512 / num + offset)) >> 10
Dim yy As Single = SinTable.get().sin((i * 512 / num + offset)) >> 10
screen.blt(heart, CInt(x - 50 / 4 + xx), CInt(y - 40 / 4 + yy), dmy, s)
i += 1
End While
End If
End If
Else
Dim s As New Size
Dim dmy As New Rect
s.setSize(CInt(heart.getWidth() * deadtime / 100), CInt(heart.getHeight() * deadtime / 100))
screen.blt(heart, CInt(x - 50 * deadtime / 100), CInt(y - 40 * deadtime / 100), dmy, s)
End If
End If
' ♪の描画
screen.blendSrcAlpha()
For i As Integer = 0 To tama.Length - 1
tama(i).onDraw(screen, onpu)
Next
screen.setColor(0, 0, 200)
screen.setLineWidth(5)
screen.drawString(StringConv.toConvHelpperU(score, 10, 8, " "c), 10, 10, 30)
' if (!bgm.isPlay()) break;
If play.time() >= 1000 * (60 * 4 + 2 + 5) Then
' goto replay; // 曲end
If Not gameover Then
gameover = True
extend = Integer.MaxValue ' extendしない
score = score * 2 + myleft * 1000000
End If
End If
If highscore < score Then
highscore = score
End If
screen.setColor(0, 200, 0)
screen.drawString(StringConv.toConvHelpperU(highscore, 10, 8, " "c), 320, 10, 30)
If bc <> 0 Then
screen.setColor(255, 0, 0)
screen.drawString(StringConv.toDec(bn) + "x" + StringConv.toDec(br), bx, by - 50, 30)
End If
time.update()
screen.update()
ContinueWhile1:
End While
Catch e As Exception
Console.WriteLine("例外が発生 : {0}", e.Message)
End Try
Return 0
End Function
End Class
End Namespace