/
arti4
/
IndividualProject
Обзор
Документация
Войти
/
arti4
/
IndividualProject
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
MyGame/app/src/main/java/com/example/mygame/GameView.java
1 001 строка
36 KB
arti4
upload files
18 ноя 2025, 12:41
18 ноя 2025, 12:41
f3a2f8a
Код
Авторство
О чём код?
package com.example.mygame; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.app.Activity; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Random; import java.util.Set; import android.graphics.Path; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.RelativeLayout; import androidx.appcompat.app.AppCompatActivity; import org.jbox2d.common.Vec2; public class GameView extends SurfaceView implements SurfaceHolder.Callback { private static final String TAG = "GameView"; private GameThread thread; private Motorcycle motorcycle; private List<RoadSegment> roadSegments; private List<Bitmap> roadBitmaps; private int screenWidth, screenHeight; private Random random; private boolean gameRunning = true; private int score = 0; private Paint textPaint; private float scaleFactor = 1.0f; private int highScore = 0; private PhysicsWorld physicsWorld; private boolean isTouching = false; private float motorcycleTargetX = 0f; private float touchStartX = 0f; private final float MOTORCYCLE_CAMERA_OFFSET = 0.3f; private float cameraX = 0f; private boolean isMotorcycleDestroyed = false; private float motorcycleDestroyTimer = 0f; private final float MOTORCYCLE_DESTROY_DURATION = 60f; private List<MotorcycleParticle> motorcycleParticles = new ArrayList<>(); private boolean showExplosionEffect = false; private int explosionCounter = 0; private int frameCount = 0; private final float GROUND_LEVEL = 0.7f; private SoundManager soundManager; private boolean isPaused = false; private static final int PAUSE_MENU_REQUEST = 1001; private static final String HIGH_SCORE_PREF = "high_score"; private int totalCrystals = 0; private int collectedCrystals = 0; private static final String CRYSTALS_PREF = "collected_crystals"; private static final String TOTAL_CRYSTALS_PREF = "total_crystals"; private Set<Integer> collectedCrystalIds = new HashSet<>(); private int currentFragmentType = -1; private int fragmentProgress = 0; private int[] currentFragmentSequence; private BackgroundLayer backgroundLayer; public GameView(Context context) { super(context); getHolder().addCallback(this); setFocusable(true); random = new Random(); roadSegments = new ArrayList<>(); DisplayMetrics displayMetrics = new DisplayMetrics(); ((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); screenWidth = displayMetrics.widthPixels; screenHeight = displayMetrics.heightPixels; scaleFactor = Math.min((float)screenWidth / 1080f, (float)screenHeight / 1920f); physicsWorld = new PhysicsWorld(); soundManager = createSoundManagerWithSettings(); backgroundLayer = new BackgroundLayer(context, screenWidth, screenHeight, 2.0f); setupPaints(); initBitmaps(); setupGame(); } public void showPauseMenu() { if (!isPaused) { pauseGame(); Intent intent = new Intent(getContext(), PauseMenuActivity.class); ((Activity) getContext()).startActivityForResult(intent, PAUSE_MENU_REQUEST); ((Activity) getContext()).overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); } } private SoundManager createSoundManagerWithSettings() { SharedPreferences prefs = getContext().getSharedPreferences("SoundSettings", Context.MODE_PRIVATE); int musicVolume = prefs.getInt("music_volume", 50); int effectsVolume = prefs.getInt("effects_volume", 50); float musicVolumeFloat = musicVolume / 100.0f; float effectsVolumeFloat = effectsVolume / 100.0f; return new SoundManager(getContext(), musicVolumeFloat, effectsVolumeFloat); } private void setupPaints() { textPaint = new Paint(); textPaint.setColor(Color.WHITE); textPaint.setTextSize(50 * scaleFactor); textPaint.setAntiAlias(true); textPaint.setShadowLayer(5, 3, 3, Color.BLACK); } private void initBitmaps() { try { RoadGenerator roadGenerator = new RoadGenerator(screenWidth, scaleFactor); applyMapColorScheme(roadGenerator); roadBitmaps = new ArrayList<>(); for (int type = 0; type < roadGenerator.getTypeCount(); type++) { Bitmap roadBitmap = roadGenerator.generateRoadSegment(type); roadBitmaps.add(roadBitmap); } } catch (Exception e) { createFallbackBitmaps(); } } private void applyMapColorScheme(RoadGenerator roadGenerator) { SharedPreferences prefs = getContext().getSharedPreferences("MapPrefs", Context.MODE_PRIVATE); int selectedMap = prefs.getInt("selected_map", 0); int[][] colorSchemes = { {Color.BLACK, Color.GRAY}, {Color.WHITE, Color.LTGRAY}, {Color.rgb(52, 207, 190), Color.rgb(255, 108, 0)}, {Color.rgb(255, 0, 0), Color.rgb(0, 204, 0)}, {Color.rgb(53, 94, 59), Color.rgb(100, 130, 100)}, {Color.rgb(8, 16, 115), Color.rgb(166, 122, 0)}, {Color.rgb(0, 250, 51), Color.rgb(80, 80, 80)}, {Color.rgb(243, 253, 0), Color.rgb(121, 8, 170)}, {Color.rgb(132, 0, 77), Color.rgb(180, 205, 61)}, {Color.rgb(139, 0, 0), Color.BLACK} }; if (selectedMap >= 0 && selectedMap < colorSchemes.length) { roadGenerator.setColorScheme(colorSchemes[selectedMap][0], colorSchemes[selectedMap][1]); } } private void createFallbackBitmaps() { RoadGenerator roadGenerator = new RoadGenerator(screenWidth, scaleFactor); applyMapColorScheme(roadGenerator); roadBitmaps = new ArrayList<>(); for (int type = 0; type < roadGenerator.getTypeCount(); type++) { Bitmap roadBitmap = roadGenerator.generateRoadSegment(type); roadBitmaps.add(roadBitmap); } } public void pauseGameMusic() { if (soundManager != null) { soundManager.stopSound(SoundManager.SOUND_GAME); } } public void resumeGameMusic() { if (soundManager != null) { soundManager.playSound(SoundManager.SOUND_GAME); } } private void setupGame() { try { currentFragmentType = -1; fragmentProgress = 0; currentFragmentSequence = null; float groundLevel = screenHeight * GROUND_LEVEL; float startX = screenWidth * MOTORCYCLE_CAMERA_OFFSET - 50 * scaleFactor; float startY = groundLevel - 120 * scaleFactor; loadHighScore(); motorcycleTargetX = startX; cameraX = 0f; int selectedMotorcycle = getSelectedMotorcycleType(); motorcycle = new Motorcycle(physicsWorld, (int)startX, (int)startY, screenWidth, screenHeight, scaleFactor, selectedMotorcycle, getContext()); isMotorcycleDestroyed = false; motorcycleDestroyTimer = 0f; motorcycleParticles.clear(); showExplosionEffect = false; explosionCounter = 0; generateInitialRoad(); loadCrystalsState(); if (soundManager != null) { soundManager.playSound(SoundManager.SOUND_GAME); } } catch (Exception e) { e.printStackTrace(); } } private int getSelectedMotorcycleType() { SharedPreferences prefs = getContext().getSharedPreferences("MotorcyclePrefs", Context.MODE_PRIVATE); int selected = prefs.getInt("selected_motorcycle", 0); if (!Motorcycle.isMotorcycleAvailable(getContext(), selected)) { selected = 0; prefs.edit().putInt("selected_motorcycle", 0).apply(); } return selected; } private void updateCamera() { if (motorcycle.getSpeed() > 0.1f) { float motorcycleCenterX = motorcycle.getX() + 50 * scaleFactor; cameraX = motorcycleCenterX - screenWidth * MOTORCYCLE_CAMERA_OFFSET; } } private void generateInitialRoad() { int currentX = 0; for (int i = 0; i < 4; i++) { addRoadSegment(currentX, 0); currentX += roadBitmaps.get(0).getWidth(); } } private void addRoadSegment(int x, int type) { float groundLevel = screenHeight * GROUND_LEVEL; int segmentHeight = roadBitmaps.get(type).getHeight(); int segmentY = (int)(groundLevel - segmentHeight); int fillColor = getCurrentFillColor(); int strokeColor = getCurrentStrokeColor(); RoadSegment segment = new RoadSegment(roadBitmaps.get(type), x, segmentY, type, screenWidth, physicsWorld, fillColor, strokeColor, getContext()); segment.setScaleFactor(scaleFactor); roadSegments.add(segment); } private int getCurrentFillColor() { SharedPreferences prefs = getContext().getSharedPreferences("MapPrefs", Context.MODE_PRIVATE); int selectedMap = prefs.getInt("selected_map", 0); int[][] colorSchemes = { {Color.BLACK, Color.GRAY}, {Color.WHITE, Color.LTGRAY}, {Color.rgb(52, 207, 190), Color.rgb(255, 108, 0)}, {Color.rgb(255, 0, 0), Color.rgb(0, 204, 0)}, {Color.rgb(53, 94, 59), Color.rgb(100, 130, 100)}, {Color.rgb(8, 16, 115), Color.rgb(166, 122, 0)}, {Color.rgb(0, 250, 51), Color.rgb(80, 80, 80)}, {Color.rgb(243, 253, 0), Color.rgb(121, 8, 170)}, {Color.rgb(132, 0, 77), Color.rgb(180, 205, 61)}, {Color.rgb(139, 0, 0), Color.BLACK} }; if (selectedMap >= 0 && selectedMap < colorSchemes.length) { return colorSchemes[selectedMap][0]; } return Color.BLACK; } private int getCurrentStrokeColor() { SharedPreferences prefs = getContext().getSharedPreferences("MapPrefs", Context.MODE_PRIVATE); int selectedMap = prefs.getInt("selected_map", 0); int[][] colorSchemes = { {Color.BLACK, Color.GRAY}, {Color.WHITE, Color.LTGRAY}, {Color.rgb(52, 207, 190), Color.rgb(255, 108, 0)}, {Color.rgb(255, 0, 0), Color.rgb(0, 204, 0)}, {Color.rgb(53, 94, 59), Color.rgb(100, 130, 100)}, {Color.rgb(8, 16, 115), Color.rgb(166, 122, 0)}, {Color.rgb(0, 250, 51), Color.rgb(80, 80, 80)}, {Color.rgb(243, 253, 0), Color.rgb(121, 8, 170)}, {Color.rgb(132, 0, 77), Color.rgb(180, 205, 61)}, {Color.rgb(139, 0, 0), Color.BLACK} }; if (selectedMap >= 0 && selectedMap < colorSchemes.length) { return colorSchemes[selectedMap][1]; } return Color.GRAY; } @Override public boolean onTouchEvent(MotionEvent event) { if (isPaused) { return true; } if (isMotorcycleDestroyed) { return true; } switch (event.getAction()) { case MotionEvent.ACTION_DOWN: isTouching = true; touchStartX = event.getX(); motorcycleTargetX = touchStartX; return true; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: isTouching = false; return true; case MotionEvent.ACTION_MOVE: if (isTouching) { float touchX = event.getX(); motorcycleTargetX = touchX; float minX = 50; float maxX = screenWidth - 100 * scaleFactor - 50; motorcycleTargetX = Math.max(minX, Math.min(motorcycleTargetX, maxX)); } return true; } return super.onTouchEvent(event); } public void updateGame() { if (!gameRunning || isPaused) return; try { physicsWorld.update(1/60f); if (isMotorcycleDestroyed) { updateMotorcycleDestruction(); return; } float motorcycleSpeed = motorcycle != null ? Math.max(0f, motorcycle.getSpeed()) : 2.0f; backgroundLayer.setSpeed(motorcycleSpeed); if (frameCount % 3 == 0) { backgroundLayer.update(cameraX); } if (isMotorcycleDestroyed) { return; } checkCrystalCollection(); motorcycle.update(); updateCamera(); updateRoadSegments(); updateMotorcycleControl(); checkRampInteraction(); checkCollisions(); if (frameCount % 2 == 0) { manageRoadGeneration(); } updateScore(); frameCount++; } catch (Exception e) { Log.e(TAG, "Error in updateGame: " + e.getMessage()); } } private void checkCrystalCollection() { if (isMotorcycleDestroyed) return; Rect motorcycleRect = motorcycle.getRect(); for (RoadSegment segment : roadSegments) { for (Crystal crystal : segment.getCrystals()) { if (crystal.checkCollection(motorcycleRect)) { collectedCrystals++; collectedCrystalIds.add(crystal.getCrystalId()); } } } } private void loadCrystalsState() { SharedPreferences prefs = getContext().getSharedPreferences("GamePrefs", Context.MODE_PRIVATE); collectedCrystals = prefs.getInt(CRYSTALS_PREF, 0); totalCrystals = prefs.getInt(TOTAL_CRYSTALS_PREF, 0); String collectedIds = prefs.getString("collected_crystal_ids", ""); if (!collectedIds.isEmpty()) { String[] ids = collectedIds.split(","); for (String id : ids) { if (!id.isEmpty()) { collectedCrystalIds.add(Integer.parseInt(id)); } } } } private void saveCrystalsState() { SharedPreferences prefs = getContext().getSharedPreferences("GamePrefs", Context.MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putInt(CRYSTALS_PREF, collectedCrystals); editor.putInt(TOTAL_CRYSTALS_PREF, totalCrystals); StringBuilder idsBuilder = new StringBuilder(); for (Integer id : collectedCrystalIds) { idsBuilder.append(id).append(","); } if (idsBuilder.length() > 0) { idsBuilder.setLength(idsBuilder.length() - 1); } editor.putString("collected_crystal_ids", idsBuilder.toString()); editor.apply(); } private void checkRampInteraction() { if (isMotorcycleDestroyed || !motorcycle.isOnGround()) return; Rect motorcycleRect = motorcycle.getRect(); boolean wasOnRamp = false; float motorcycleSpeed = motorcycle.getSpeed(); for (RoadSegment segment : roadSegments) { if (segment.getType() == 1 || segment.getType() == 5 || segment.getType() == 6) { boolean onRamp = false; if (segment.getType() == 6) { onRamp = segment.isCarOnTriangleRamp(motorcycleRect); } else { onRamp = segment.isCarOnRamp(motorcycleRect); } if (onRamp) { wasOnRamp = true; boolean isLeaving = false; if (segment.getType() == 6) { isLeaving = segment.isCarLeavingTriangleRamp(motorcycleRect); } else { isLeaving = segment.isCarLeavingRamp(motorcycleRect); } if (isLeaving) { if (segment.getType() == 6) { if (motorcycleSpeed >= 5.0f) { float jumpPower = segment.getTriangleRampJumpPower(); motorcycle.applyJump(jumpPower); } } else { float jumpPower = segment.getRampJumpPower(); motorcycle.applyJump(jumpPower); } break; } } } } } private void updateMotorcycleControl() { motorcycle.moveHorizontal(motorcycleTargetX); if (isTouching) { if (motorcycle.isOnGround()) { motorcycle.applyEngineForce(true); } else { motorcycle.rotateInAir(); } } else { motorcycle.applyEngineForce(false); } } private void updateMotorcycleDestruction() { motorcycleDestroyTimer++; Iterator<MotorcycleParticle> iterator = motorcycleParticles.iterator(); while (iterator.hasNext()) { MotorcycleParticle particle = iterator.next(); particle.update(); if (particle.isDead()) { iterator.remove(); } } if (showExplosionEffect) { explosionCounter++; if (explosionCounter > 30) { showExplosionEffect = false; } } if (motorcycleDestroyTimer > MOTORCYCLE_DESTROY_DURATION) { gameOver(); } } private void destroyMotorcycle() { if (isMotorcycleDestroyed) return; isMotorcycleDestroyed = true; motorcycleDestroyTimer = 0f; showExplosionEffect = true; explosionCounter = 0; if (soundManager != null) { soundManager.playSound(SoundManager.SOUND_EXPLOSION); } } private void updateRoadSegments() { Iterator<RoadSegment> iterator = roadSegments.iterator(); while (iterator.hasNext()) { RoadSegment segment = iterator.next(); segment.update(); if (segment.getRight() < cameraX - 1000) { segment.destroyPhysicsBodies(); iterator.remove(); } } } public void updateSoundVolume() { if (soundManager != null) { SharedPreferences prefs = getContext().getSharedPreferences("SoundSettings", Context.MODE_PRIVATE); int musicVolume = prefs.getInt("music_volume", 50); int effectsVolume = prefs.getInt("effects_volume", 50); float musicVolumeFloat = musicVolume / 100.0f; float effectsVolumeFloat = effectsVolume / 100.0f; soundManager.setMusicVolume(musicVolumeFloat); soundManager.setEffectsVolume(effectsVolumeFloat); } } private void checkCollisions() { if (isMotorcycleDestroyed) return; Rect motorcycleRect = motorcycle.getRect(); if (motorcycle.isUpsideDown() && motorcycle.isOnGround()) { Vec2 velocity = motorcycle.physicsBody.getLinearVelocity(); if (Math.abs(velocity.y) > 8f || Math.abs(motorcycle.getRotation()) > 150f) { destroyMotorcycle(); return; } } for (RoadSegment segment : roadSegments) { if (segment.getRight() < cameraX - 500 || segment.getX() > cameraX + screenWidth + 500) { continue; } if (segment.getType() == 2 && segment.hasCollisionWithCar(motorcycleRect)) { gameOver(); return; } else if (segment.getType() == 3) { boolean isHitting = segment.isHammerHittingCar(motorcycleRect); if (isHitting) { destroyMotorcycle(); return; } } } if (motorcycle.getY() > screenHeight + 500) { gameOver(); } } private void manageRoadGeneration() { if (roadSegments.isEmpty()) { generateInitialRoad(); return; } RoadSegment lastSegment = getRightmostSegment(); if (lastSegment != null && lastSegment.getRight() < cameraX + screenWidth + 2000) { int newX = lastSegment.getRight(); int type = getNextSegmentType(); addRoadSegment(newX, type); } } private int getNextSegmentType() { if (currentFragmentType != -1 && fragmentProgress < currentFragmentSequence.length) { int type = currentFragmentSequence[fragmentProgress]; fragmentProgress++; if (fragmentProgress >= currentFragmentSequence.length) { currentFragmentType = -1; fragmentProgress = 0; currentFragmentSequence = null; } return type; } return getRandomSegmentType(); } private RoadSegment getRightmostSegment() { if (roadSegments.isEmpty()) return null; RoadSegment rightmost = roadSegments.get(0); for (RoadSegment segment : roadSegments) { if (segment.getRight() > rightmost.getRight()) { rightmost = segment; } } return rightmost; } private int getRandomSegmentType() { if (roadSegments.size() < 4) { return 0; } int lastType = getLastSegmentType(0); int secondLastType = getLastSegmentType(1); if (lastType != 0 && secondLastType == 0) { return 0; } if (lastType != 0) { return 0; } if (lastType == 0 && secondLastType == 0) { int fragmentType = getRandomFragmentType(); startFragment(fragmentType); return getNextSegmentType(); } return 0; } private void startFragment(int fragmentType) { currentFragmentType = fragmentType; fragmentProgress = 0; switch (fragmentType) { case 1: // трамплин, препятствие, дорога currentFragmentSequence = new int[]{1, 2, 0}; break; case 2: // длинный трамплин, 3 препятствия, дорога currentFragmentSequence = new int[]{5, 2, 2,2, 2, 0}; break; case 3: // молот, дорога currentFragmentSequence = new int[]{3, 0}; break; case 4: // молот, молот, дорога currentFragmentSequence = new int[]{3, 3, 0}; break; case 5: // движущаяся платформа, дорога currentFragmentSequence = new int[]{4, 0}; break; case 6: // движущаяся платформа, движущаяся платформа, дорога currentFragmentSequence = new int[]{4, 4, 0}; break; } } private int getRandomFragmentType() { int[][] weightedFragments = { {1, 30}, {2, 25}, {3, 20}, {4, 15}, {5, 5}, }; int totalWeight = 0; for (int[] fragment : weightedFragments) { totalWeight += fragment[1]; } int randomValue = random.nextInt(totalWeight); int currentWeight = 0; for (int[] fragment : weightedFragments) { currentWeight += fragment[1]; if (randomValue < currentWeight) { return fragment[0]; } } return 1; } private int getLastSegmentType(int offset) { if (roadSegments.size() > offset) { return roadSegments.get(roadSegments.size() - 1 - offset).getType(); } return -1; } private void updateScore() { score += (int)Math.abs(motorcycle.getSpeed()) / 2; if (score > highScore) { highScore = score; } } private void gameOver() { gameRunning = false; if (score > highScore) { highScore = score; saveHighScore(); } saveCrystalsState(); showGameOverScreen(); } private void showGameOverScreen() { gameRunning = false; if (score > highScore) { highScore = score; saveHighScore(); } saveCrystalsState(); if (soundManager != null) { soundManager.stopAllSounds(); soundManager.stopSound(SoundManager.SOUND_GAME); soundManager.release(); } if (thread != null) { thread.setRunning(false); } Intent intent = new Intent(getContext(), GameOverActivity.class); intent.putExtra("score", score); intent.putExtra("high_score", highScore); getContext().startActivity(intent); if (getContext() instanceof Activity) { ((Activity) getContext()).finish(); } } public void stopGameMusic() { if (soundManager != null) { soundManager.stopSound(SoundManager.SOUND_GAME); } } private void loadHighScore() { SharedPreferences prefs = getContext().getSharedPreferences("GamePrefs", Context.MODE_PRIVATE); highScore = prefs.getInt(HIGH_SCORE_PREF, 0); } private void saveHighScore() { SharedPreferences prefs = getContext().getSharedPreferences("GamePrefs", Context.MODE_PRIVATE); prefs.edit().putInt(HIGH_SCORE_PREF, highScore).apply();; } public boolean isPaused() { return isPaused; } public void onBackPressed() { if (!gameRunning && isMotorcycleDestroyed) { ((Activity)getContext()).finish(); } } public void handleBackPressed() { if (!gameRunning && isMotorcycleDestroyed) { ((AppCompatActivity)getContext()).getOnBackPressedDispatcher().onBackPressed(); } else { ((AppCompatActivity)getContext()).getOnBackPressedDispatcher().onBackPressed(); } } @Override public void draw(Canvas canvas) { super.draw(canvas); if (canvas == null) return; try { if (backgroundLayer != null) { backgroundLayer.draw(canvas, cameraX); } if (!isPaused) { canvas.save(); canvas.translate(-cameraX, 0); for (RoadSegment segment : roadSegments) { segment.draw(canvas); } if (!isMotorcycleDestroyed && motorcycle != null) { motorcycle.draw(canvas); } canvas.restore(); for (MotorcycleParticle particle : motorcycleParticles) { particle.draw(canvas); } if (showExplosionEffect && motorcycle != null) { drawExplosionEffect(canvas, motorcycle.getX() + 50 * scaleFactor - cameraX, motorcycle.getY() + 30 * scaleFactor); } } drawUI(canvas); } catch (Exception e) { Log.d(TAG, "Error in draw: " + e.getMessage()); } } private void drawExplosionEffect(Canvas canvas, float centerX, float centerY) { Paint explosionPaint = new Paint(); float progress = explosionCounter / 30f; float maxRadius = 80 * scaleFactor * progress; explosionPaint.setColor(Color.argb(200, 255, 165, 0)); canvas.drawCircle(centerX, centerY, maxRadius * 0.8f, explosionPaint); explosionPaint.setColor(Color.argb(150, 255, 255, 0)); canvas.drawCircle(centerX, centerY, maxRadius * 0.5f, explosionPaint); explosionPaint.setColor(Color.argb(100, 255, 0, 0)); explosionPaint.setStyle(Paint.Style.STROKE); explosionPaint.setStrokeWidth(5 * scaleFactor); canvas.drawCircle(centerX, centerY, maxRadius, explosionPaint); } private void drawUI(Canvas canvas) { /* canvas.drawText("Score: " + score, 50, 80, textPaint); canvas.drawText("High Score: " + highScore, 50, 140, textPaint); String status = motorcycle.isOnGround() ? "ON GROUND" : "IN AIR - ROTATE!"; canvas.drawText("Status: " + status, 50, 200, textPaint); canvas.drawText("Speed: " + String.format("%.1f", motorcycle.getSpeed()), 50, 260, textPaint); canvas.drawText("Rotation: " + String.format("%.0f°", motorcycle.getRotation()), 50, 320, textPaint); canvas.drawText("Motorcycle: " + motorcycle.getMotorcycleName(), 50, 380, textPaint); textPaint.setTextSize(30 * scaleFactor); canvas.drawText("Drag: MOVE BIKE", screenWidth - 350, 80, textPaint); canvas.drawText("Hold on GROUND: ACCELERATE", screenWidth - 350, 120, textPaint); canvas.drawText("Hold in AIR: ROTATE ←", screenWidth - 350, 160, textPaint); canvas.drawText("Release: BRAKE", screenWidth - 350, 200, textPaint); canvas.drawText("Avoid HAMMERS!", screenWidth - 350, 240, textPaint); textPaint.setTextSize(50 * scaleFactor); */ } @Override public void surfaceCreated(SurfaceHolder holder) { thread = new GameThread(getHolder(), this); thread.setRunning(true); thread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; if (thread != null) { thread.setRunning(false); while (retry) { try { thread.join(100); retry = false; } catch (InterruptedException e) { e.printStackTrace(); } } thread = null; } if (getContext() instanceof Activity) { Activity activity = (Activity) getContext(); if (activity.isFinishing()) { if (backgroundLayer != null) { backgroundLayer.release(); backgroundLayer = null; } if (soundManager != null) { soundManager.stopAllSounds(); } } } } public void pauseGame() { isPaused = true; pauseGameMusic(); if (thread != null) { thread.setRunning(false); } } public void resumeGame() { isPaused = false; resumeGameMusic(); if (backgroundLayer == null) { DisplayMetrics displayMetrics = new DisplayMetrics(); ((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); int screenWidth = displayMetrics.widthPixels; int screenHeight = displayMetrics.heightPixels; backgroundLayer = new BackgroundLayer(getContext(), screenWidth, screenHeight, 2.0f); } if (thread != null) { thread.setRunning(true); } else { thread = new GameThread(getHolder(), this); thread.setRunning(true); thread.start(); } } public void onActivityResult(int requestCode, int resultCode) { if (requestCode == PAUSE_MENU_REQUEST) { if (resultCode == Activity.RESULT_OK) { updateSoundVolume(); resumeGame(); } } } public void restartGame() { gameRunning = true; score = 0; isTouching = false; isMotorcycleDestroyed = false; motorcycleDestroyTimer = 0f; motorcycleParticles.clear(); showExplosionEffect = false; explosionCounter = 0; roadSegments.clear(); physicsWorld.dispose(); physicsWorld = new PhysicsWorld(); initBitmaps(); setupGame(); if (soundManager != null) { soundManager.stopSound(SoundManager.SOUND_GAME); soundManager.playSound(SoundManager.SOUND_GAME); } } private class GameThread extends Thread { private SurfaceHolder surfaceHolder; private GameView gameView; private boolean running; private long lastTime = System.currentTimeMillis(); public GameThread(SurfaceHolder holder, GameView view) { surfaceHolder = holder; gameView = view; } public void setRunning(boolean running) { this.running = running; } @Override public void run() { while (running) { if (!gameRunning || isPaused) { try { Thread.sleep(16); } catch (InterruptedException e) { e.printStackTrace(); } continue; } long currentTime = System.currentTimeMillis(); long elapsedTime = currentTime - lastTime; Canvas canvas = null; try { canvas = surfaceHolder.lockCanvas(); if (canvas != null) { synchronized (surfaceHolder) { gameView.updateGame(); gameView.draw(canvas); } } } finally { if (canvas != null) { surfaceHolder.unlockCanvasAndPost(canvas); } } try { Thread.sleep(16); } catch (InterruptedException e) { e.printStackTrace(); } lastTime = currentTime; } } } private class MotorcycleParticle { float x, y; int color; float velocityX, velocityY; float size; float life = 1.0f; float decayRate; public MotorcycleParticle(float startX, float startY, int color, float velocityX, float velocityY, float size) { this.x = startX; this.y = startY; this.color = color; this.velocityX = velocityX; this.velocityY = velocityY; this.size = size; this.decayRate = 0.02f + random.nextFloat() * 0.03f; } public void update() { x += velocityX; y += velocityY; velocityY += 0.1f; life -= decayRate; } public void draw(Canvas canvas) { if (life <= 0) return; Paint particlePaint = new Paint(); particlePaint.setColor(color); particlePaint.setAlpha((int)(255 * life)); canvas.drawCircle(x, y, size * life, particlePaint); } public boolean isDead() { return life <= 0; } } }