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