1
\$\begingroup\$

My code does not work correctly. SurfaceView shows a black screen instead of Bitmaps. I make a game loop that doesn't depend on CPU, like Fix Your Timestep!, but I can't get it to work.

public class DrawView extends SurfaceView implements SurfaceHolder.Callback {
private DrawThreat drawThread;
public  Background background;
private int height;
private int width;

public DrawView(Context context) {
    super(context);
    getHolder().addCallback(this);
    drawThread = new DrawThreat(getHolder(), this);
    setFocusable(true);
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
    Rect surface = getHolder().getSurfaceFrame();
    this.width = surface.width();
    this.height = surface.height();
    Bitmap back= BitmapFactory.decodeResource(App.getContext().getResources(), R.drawable.fon);
    back = Bitmap.createScaledBitmap(back, width, height, false);
    background = new Background(back, back, 0, 0, 0, -height);
    drawThread = new DrawThreat(getHolder(), this);
    drawThread.start();
}

public class DrawThreat extends Thread {

private final DrawView drawView;
private SurfaceHolder surfaceHolder;
private boolean running=true;
private static final int UPDATES_PER_SECOND = 25;
private static final int UPDATE_INTERVAL = 1000 / UPDATES_PER_SECOND * 1000000;
private static final int MAX_FRAMESKIP = 5;
private long nextUpdate = System.nanoTime();



public DrawThreat(SurfaceHolder surfaceHolder, DrawView drawView){
    super();
    this.surfaceHolder = surfaceHolder;
    this.drawView = drawView;


}

Tried to implement a game loop, but only a black screen is displayed

@Override
public void run(){
    while (running){
        Canvas canvas = surfaceHolder.lockCanvas();
        int skippedFrames = 0;

        while (System.nanoTime() > this.nextUpdate && skippedFrames < MAX_FRAMESKIP) {
            long delta = UPDATE_INTERVAL;
            this.drawView.update(delta);


            this.nextUpdate += UPDATE_INTERVAL;
            skippedFrames++;
        }

        double interpolation = (System.nanoTime() + UPDATE_INTERVAL - this.nextUpdate) / (double) UPDATE_INTERVAL;
        this.drawView.draw(canvas);
        surfaceHolder.unlockCanvasAndPost(canvas);
    }
    }
    }
```
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

I have also run into this issue. When my game view would start, I would see the SurfaceView area flash black, then my background graphics would render. I believe this due to the surface view initializing late, after the onSizeChanged() method being called by the system, then the surface view's canvas would be valid and able to be drawn too.

I addressed this issue in my layout file that held my SurfaceView. I put in a View with the same measurements as my surface view, so that it would show behind the SurfaceView. I also set the background color of the stand-in View widget to the most predominate color the surface view would initially show. So now upon game startup, the black flash is not shown, only the green color, followed shortly after by the background graphics that was mostly using the color in the View.

<View
    android:id="@+id/canvasStandin"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:visibility="visible"
    android:background="#FF005500"
    app:layout_constraintTop_toBottomOf="@id/dividerBottom"
    app:layout_constraintBottom_toTopOf="@id/controllerEdge"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    />
<com.mygames.gameview.MySurfaceView
    android:id="@+id/gameView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:visibility="visible"
    app:layout_constraintTop_toBottomOf="@id/dividerBottom"
    app:layout_constraintBottom_toTopOf="@id/controllerEdge"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    />

Once the game starts, you can always reference the standin component and remove it or make the visibility invisible.

\$\endgroup\$
1
  • \$\begingroup\$ I realize this answer is a bit late, but perhaps anyone else running into this issue can use my answer as a possible solution. \$\endgroup\$
    – kralvarado
    Jun 27 at 16:11
0
\$\begingroup\$

Hey so this is my loop it might need a few changes but it works for my game.

public class GameLoop extends Thread{
    public static final double MAX_UPS = %1$s;
    private static final double UPS_PERIOD = 1E+3/MAX_UPS;
    public Game game;
    public final SurfaceHolder surfaceHolder;
    
    private boolean isRunning = false;
    public double averageFPS;
    public long startTime;
    
    public GameLoop(Game game, SurfaceHolder surfaceHolder) {
        this.game = game;
        this.surfaceHolder = surfaceHolder;
    }
    public double getAverageFPS() {
        return averageFPS;
    }
    
    public void startLoop() {
        isRunning = true;
        start();
    }
    @Override
    public void run() {
        super.run();
        // Declare time and cycle count variables
        int updateCount = 0;
        int frameCount = 0;
        long elapsedTime;
        long sleepTime;
        // Game loop
        Canvas canvas = null;
        startTime = System.currentTimeMillis();
        while(isRunning) {
            // Try to update and render game
            try {
                //canvas = surfaceHolder.lockCanvas();
                canvas = surfaceHolder.lockHardwareCanvas();
                synchronized (surfaceHolder) {
                    game.update();
                    updateCount++;
                    game.draw(canvas);
                    
                }
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } finally {
                if(canvas != null) {
                    try {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                        frameCount++;
                    } catch(Exception e) {
                        e.printStackTrace();
                    }
                }
            }
            // Pause game loop to not exceed target UPS
            elapsedTime = System.currentTimeMillis() - startTime;
            sleepTime = (long) (updateCount*UPS_PERIOD - elapsedTime);
            if(sleepTime > 0) {
                try {
                    sleep(sleepTime);
                    
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            // Skip frames to keep up with target UPS
            while(sleepTime < 0 && updateCount < MAX_UPS-1) {
                game.update();
                updateCount++;
                elapsedTime = System.currentTimeMillis() - startTime;
                sleepTime = (long) (updateCount*UPS_PERIOD - elapsedTime);
            }
            // Calculates and shows the FPS (slows down gameplay when active)
            //if (game.showFPS == false) {
            elapsedTime = System.currentTimeMillis() - startTime;
            if (elapsedTime >= 1000) {
                
                averageFPS = frameCount / (1E-3 * elapsedTime);
                updateCount = 0;
                frameCount = 0;
                startTime = System.currentTimeMillis();
            }
            // }
        }
    }
    
    
    public void stopLoop() {
        isRunning = false;
        try {
            join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ This answer would be even better if it explained how this code differs from that in the question, and how those differences solve the problem. \$\endgroup\$
    – DMGregory
    Nov 19, 2022 at 18:45

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .