@@ -15,8 +15,8 @@ public partial class NeonSnake {
1515 const int KeyDown = 40 ;
1616 const int KeyLeft = 37 ;
1717 const int KeyRight = 39 ;
18- const int ScreenW = 28 * 40 ;
19- const int ScreenH = 18 * 40 + NeonSnakeGame . Snake . HudH ;
18+ double _screenW , _screenH , _gameAreaH ;
19+ int _visibleRows ;
2020
2121 CanvasComponent _canvas = null ! ;
2222 NeonSnakeGame . Snake _snake = null ! ;
@@ -41,6 +41,10 @@ static readonly (double Ox, double Oy, double Rx, double Ry, double A)[] EggSpec
4141
4242 public async Task InitializeAsync ( ) {
4343 _cellSize = _canvas . CellSize ;
44+ _screenW = _canvas . Width ;
45+ _screenH = _canvas . Height ;
46+ _gameAreaH = _screenH - NeonSnakeGame . Snake . HudH ;
47+ _visibleRows = ( int ) _gameAreaH / _cellSize ;
4448 _obstacleInset = _cellSize * 0.08 ;
4549 _obstacleSize = _cellSize - _obstacleInset * 2 ;
4650 _cacheW = _obstacleSize + ObstaclePadding * 2 ;
@@ -52,7 +56,7 @@ public async Task InitializeAsync() {
5256 await JS . InvokeVoidAsync ( "neonSnakeEggCache.renderRunningEgg" , _eggEr , _eggEh , EggCachePadding , dpr ) ;
5357 var raw = await JS . InvokeAsync < string ? > ( "localStorage.getItem" , BestScoreKey ) ;
5458 int savedBest = int . TryParse ( raw , out var v ) ? v : 0 ;
55- _snake = new NeonSnakeGame . Snake ( _cellSize ) {
59+ _snake = new NeonSnakeGame . Snake ( _cellSize , _canvas . CellsPerRow , _visibleRows ) {
5660 BestScore = savedBest
5761 } ;
5862
@@ -70,7 +74,7 @@ private async ValueTask LoopAsync(ElapsedEventArgs elapsedEvent) {
7074 var healthBefore = _snake . Health ;
7175 var deadBefore = _snake . Dead ;
7276
73- _snake . Update ( dt , ScreenW , ScreenH ) ;
77+ _snake . Update ( dt , _screenW , _gameAreaH ) ;
7478
7579 if ( _snake . Score > scoreBefore ) await PlaySoundAsync ( "neonSnakeAudio.playEatSound" ) ;
7680 if ( _snake . Health < healthBefore ) await PlaySoundAsync ( "neonSnakeAudio.playHitSound" ) ;
@@ -87,7 +91,7 @@ void HandleInput(KeyboardEventArgs e) {
8791 if ( _snake is null ) return ;
8892
8993 if ( _snake . ShowDeathScreen && ( e . Code == "Space" || e . Key == " " || e . Key == "Spacebar" ) ) {
90- _snake = new NeonSnakeGame . Snake ( _cellSize ) ;
94+ _snake = new NeonSnakeGame . Snake ( _cellSize , _canvas . CellsPerRow , _visibleRows ) ;
9195 _lastTick = DateTime . UtcNow ;
9296 return ;
9397 }
@@ -113,7 +117,7 @@ void HandleInput(KeyboardEventArgs e) {
113117
114118 void HandleTouchStart ( TouchEventArgs e ) {
115119 if ( _snake . ShowDeathScreen ) {
116- _snake = new NeonSnakeGame . Snake ( _cellSize ) ;
120+ _snake = new NeonSnakeGame . Snake ( _cellSize , _canvas . CellsPerRow , _visibleRows ) ;
117121 _lastTick = DateTime . UtcNow ;
118122 return ;
119123 }
@@ -154,17 +158,17 @@ async ValueTask PlaySoundAsync(string fn) {
154158 }
155159
156160 async ValueTask DrawAsync ( Batch2D ctx ) {
157- await ctx . ClearRectAsync ( 0 , 0 , ScreenW , ScreenH ) ;
161+ await ctx . ClearRectAsync ( 0 , 0 , _screenW , _screenH ) ;
158162
159- await ctx . FillStyleAsync ( 0 , 0 , 0 , ScreenH ,
163+ await ctx . FillStyleAsync ( 0 , 0 , 0 , _screenH ,
160164 ( 0d , Neon . BgOuter ) ,
161165 ( 1d , Neon . BgInner ) ) ;
162- await ctx . FillRectAsync ( 0 , 0 , ScreenW , ScreenH ) ;
166+ await ctx . FillRectAsync ( 0 , 0 , _screenW , _screenH ) ;
163167
164- await ctx . FillStyleAsync ( ScreenW * 0.5 , ScreenH * 0.55 , ScreenW * 0.2 , ScreenW * 0.5 , ScreenH * 0.55 , ScreenW * 0.85 ,
168+ await ctx . FillStyleAsync ( _screenW * 0.5 , _screenH * 0.55 , _screenW * 0.2 , _screenW * 0.5 , _screenH * 0.55 , _screenW * 0.85 ,
165169 ( 0d , "rgba(0,0,0,0)" ) ,
166170 ( 1d , "rgba(0,0,0,0.38)" ) ) ;
167- await ctx . FillRectAsync ( 0 , 0 , ScreenW , ScreenH ) ;
171+ await ctx . FillRectAsync ( 0 , 0 , _screenW , _screenH ) ;
168172
169173 var shakeX = 0d ;
170174 var shakeY = 0d ;
@@ -179,9 +183,8 @@ await ctx.FillStyleAsync(ScreenW * 0.5, ScreenH * 0.55, ScreenW * 0.2, ScreenW *
179183
180184 const int borderMargin = 26 ; // lineWidth/2 + shadowBlur
181185 if ( _snake . CamX < borderMargin || _snake . CamY < borderMargin ||
182- _snake . CamX + ScreenW > _snake . WorldW - borderMargin ||
183- _snake . CamY + ScreenH - NeonSnakeGame . Snake . HudH > _snake . WorldH - borderMargin )
184- {
186+ _snake . CamX + _screenW > _snake . WorldW - borderMargin ||
187+ _snake . CamY + _gameAreaH > _snake . WorldH - borderMargin ) {
185188 await DrawGridAsync ( ctx ) ;
186189 }
187190
@@ -203,7 +206,7 @@ await ctx.FillStyleAsync(ScreenW * 0.5, ScreenH * 0.55, ScreenW * 0.2, ScreenW *
203206
204207 if ( _snake . HitFlash > 0 ) {
205208 await ctx . FillStyleAsync ( $ "rgba(255, 63, 114, { _snake . HitFlash * 0.3 } )") ;
206- await ctx . FillRectAsync ( 0 , NeonSnakeGame . Snake . HudH , ScreenW , ScreenH - NeonSnakeGame . Snake . HudH ) ;
209+ await ctx . FillRectAsync ( 0 , NeonSnakeGame . Snake . HudH , _screenW , _gameAreaH ) ;
207210 }
208211
209212 await DrawHudAsync ( ctx ) ;
@@ -227,8 +230,8 @@ async ValueTask DrawObstaclesAsync(Batch2D ctx) {
227230 var ox = o . X * _cellSize + _obstacleInset - ObstaclePadding ;
228231 var oy = o . Y * _cellSize + _obstacleInset - ObstaclePadding ;
229232
230- if ( ox + _cacheW < _snake . CamX - _cellSize || ox > _snake . CamX + ScreenW + _cellSize ) continue ;
231- if ( oy + _cacheW < _snake . CamY - _cellSize || oy > _snake . CamY + ScreenH + _cellSize ) continue ;
233+ if ( ox + _cacheW < _snake . CamX - _cellSize || ox > _snake . CamX + _screenW + _cellSize ) continue ;
234+ if ( oy + _cacheW < _snake . CamY - _cellSize || oy > _snake . CamY + _gameAreaH + _cellSize ) continue ;
232235
233236 await ctx . DrawImageAsync ( "obstacleCache" , ox , oy , _cacheW , _cacheW ) ;
234237 }
@@ -241,8 +244,8 @@ async ValueTask DrawEggsAsync(Batch2D ctx) {
241244 var halfCache = _eggCacheSize * 0.5 ;
242245 var camX = _snake . CamX ;
243246 var camY = _snake . CamY ;
244- var viewRight = camX + ScreenW ;
245- var viewBottom = camY + ScreenH ;
247+ var viewRight = camX + _screenW ;
248+ var viewBottom = camY + _gameAreaH ;
246249 var cullMargin = _cellSize * 1.5 ;
247250 var glowMargin = _cellSize * 0.5 ;
248251
@@ -700,9 +703,9 @@ async ValueTask DrawHudAsync(Batch2D ctx) {
700703 await ctx . FillStyleAsync ( 0 , 0 , 0 , NeonSnakeGame . Snake . HudH ,
701704 ( 0d , Neon . HudBgTop ) ,
702705 ( 1d , Neon . HudBgBottom ) ) ;
703- await ctx . FillRectAsync ( 0 , 0 , ScreenW , NeonSnakeGame . Snake . HudH ) ;
706+ await ctx . FillRectAsync ( 0 , 0 , _screenW , NeonSnakeGame . Snake . HudH ) ;
704707 await ctx . FillStyleAsync ( Neon . HudLine ) ;
705- await ctx . FillRectAsync ( 0 , NeonSnakeGame . Snake . HudH - 2 , ScreenW , 2 ) ;
708+ await ctx . FillRectAsync ( 0 , NeonSnakeGame . Snake . HudH - 2 , _screenW , 2 ) ;
706709
707710 await ctx . FillStyleAsync ( Neon . TextPrimary ) ;
708711 await ctx . ShadowColorAsync ( Neon . SnakeGlow ) ;
@@ -716,7 +719,7 @@ await ctx.FillStyleAsync(0, 0, 0, NeonSnakeGame.Snake.HudH,
716719 var heartSize = 14d ;
717720 var heartGap = 36d ;
718721 var heartsW = 3 * heartGap ;
719- var hx0 = ( ScreenW - heartsW ) / 2d + heartGap / 2d ;
722+ var hx0 = ( _screenW - heartsW ) / 2d + heartGap / 2d ;
720723 var hy = 26d ;
721724 for ( var i = 0 ; i < 3 ; i ++ ) {
722725 var cx = hx0 + i * heartGap ;
@@ -727,7 +730,7 @@ await ctx.FillStyleAsync(0, 0, 0, NeonSnakeGame.Snake.HudH,
727730 await ctx . FontAsync ( "18px monospace" ) ;
728731 await ctx . TextAlignAsync ( TextAlign . Right ) ;
729732 if ( _snake . BestScore != _lastBestScore ) { _lastBestScore = _snake . BestScore ; _bestText = $ "Best: { _snake . BestScore } "; }
730- await ctx . FillTextAsync ( _bestText , ScreenW - 15 , 33 ) ;
733+ await ctx . FillTextAsync ( _bestText , _screenW - 15 , 33 ) ;
731734 await ctx . TextAlignAsync ( TextAlign . Left ) ;
732735 }
733736
@@ -761,32 +764,32 @@ async ValueTask DrawHeartAsync(Batch2D ctx, double cx, double cy, double size, s
761764 }
762765
763766 async ValueTask DrawDeathScreenAsync ( Batch2D ctx ) {
764- await ctx . FillStyleAsync ( 0 , NeonSnakeGame . Snake . HudH , 0 , ScreenH ,
767+ await ctx . FillStyleAsync ( 0 , NeonSnakeGame . Snake . HudH , 0 , _screenH ,
765768 ( 0d , "rgba(7,12,31,0.7)" ) ,
766769 ( 1d , "rgba(2,4,13,0.8)" ) ) ;
767- await ctx . FillRectAsync ( 0 , NeonSnakeGame . Snake . HudH , ScreenW , ScreenH - NeonSnakeGame . Snake . HudH ) ;
770+ await ctx . FillRectAsync ( 0 , NeonSnakeGame . Snake . HudH , _screenW , _gameAreaH ) ;
768771
769772 await ctx . FillStyleAsync ( Neon . Danger ) ;
770773 await ctx . ShadowColorAsync ( Neon . DangerGlow ) ;
771774 await ctx . ShadowBlurAsync ( 18 ) ;
772775 await ctx . FontAsync ( "bold 48px monospace" ) ;
773776 await ctx . TextAlignAsync ( TextAlign . Center ) ;
774- var cy = NeonSnakeGame . Snake . HudH + ( ScreenH - NeonSnakeGame . Snake . HudH ) / 2d ;
775- await ctx . FillTextAsync ( "Game Over" , ScreenW / 2d , cy - 30 ) ;
777+ var cy = NeonSnakeGame . Snake . HudH + _gameAreaH / 2d ;
778+ await ctx . FillTextAsync ( "Game Over" , _screenW / 2d , cy - 30 ) ;
776779
777780 await ctx . ShadowBlurAsync ( 0 ) ;
778781 await ctx . FillStyleAsync ( "#f2f8ff" ) ;
779782 await ctx . FontAsync ( "24px monospace" ) ;
780- await ctx . FillTextAsync ( $ "Score: { _snake . Score } ", ScreenW / 2d , cy + 15 ) ;
783+ await ctx . FillTextAsync ( $ "Score: { _snake . Score } ", _screenW / 2d , cy + 15 ) ;
781784 if ( _snake . Score >= _snake . BestScore && _snake . Score > 0 ) {
782785 await ctx . FillStyleAsync ( Neon . TextAccent ) ;
783786 await ctx . FontAsync ( "20px monospace" ) ;
784- await ctx . FillTextAsync ( "New Best!" , ScreenW / 2d , cy + 45 ) ;
787+ await ctx . FillTextAsync ( "New Best!" , _screenW / 2d , cy + 45 ) ;
785788 }
786789
787790 await ctx . FontAsync ( "18px monospace" ) ;
788791 await ctx . FillStyleAsync ( Neon . TextMuted ) ;
789- await ctx . FillTextAsync ( " Press SPACE to restart", ScreenW / 2d , cy + 80 ) ;
792+ await ctx . FillTextAsync ( _isMobile ? "Tap to restart" : " Press SPACE to restart", _screenW / 2d , cy + 80 ) ;
790793 await ctx . TextAlignAsync ( TextAlign . Start ) ;
791794 }
792795}
0 commit comments