В настоящее время я учусь создавать простую игру для Android с использованием SurfaceView. В настоящее время я сталкиваюсь с двумя проблемами:
- SurfaceView обрабатывает весь экран, когда не должен, так как я применил к нему
layout_weight
. - SurfaceViewничего не рендерит, просто огромный белый экран.
Вот мой код
GameFragment.java
package com.example.tubes_2.fragments;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.example.tubes_2.R;
import com.example.tubes_2.fragments.surfaceView.GameView;
import com.example.tubes_2.model.Attack;
import com.example.tubes_2.model.Ship;
import com.example.tubes_2.presenter.GameStatus;
import com.example.tubes_2.template.DifficultyTemplate;
import java.util.List;
/**
* A simple {@link Fragment} subclass.
*/
public class GameFragment extends Fragment {
DrawerThread drawerThread;
LogicThread logicThread;
GameView gameView;
ImageView playerView;
ImageView enemyView;
GameStatus gameStatus;
public GameFragment() {
// Required empty public constructor
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_game, container, false);
// this.difficulty = this.getArguments().getParcelable("difficulty");
this.gameStatus = new GameStatus(this, null);
LinearLayout gameWrapper = view.findViewById(R.id.gameView);
this.gameView = new GameView(this.getContext(), this);
gameWrapper.addView(this.gameView);
this.playerView = view.findViewById(R.id.playerView);
this.enemyView = view.findViewById(R.id.enemyView);
return view;
}
public void initializeGame() {
this.gameStatus.initializeGame();
}
public void startGame() {
this.drawerThread = new DrawerThread(this);
this.drawerThread.run();
}
public void endGame() {
this.gameStatus.endGame();
}
public static GameFragment newInstance(DifficultyTemplate difficulty) {
GameFragment fragment = new GameFragment();
Bundle args = new Bundle();
args.putParcelable("difficulty", difficulty);
fragment.setArguments(args);
return fragment;
}
public GameView getGameView() {
return gameView;
}
}
фрагмент_игры.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:weightSum="7">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="6">
<LinearLayout
android:orientation="horizontal"
android:id="@+id/gameView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|start"
android:layout_margin="@dimen/medium_margin"
android:backgroundTint="@color/flame_off"
app:borderWidth="0dp"
android:src="@drawable/ic_iconmonstr_whats_hot_2" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:orientation="horizontal"
android:layout_weight="1"
android:background="@color/bgSmoke"
android:padding="@dimen/small_padding"
android:weightSum="3">
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="1">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textSize="@dimen/label_size"
android:text="@string/player_label"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:textSize="@dimen/label_size"
android:text="@string/enemy_label" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_weight="2">
<ImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="@dimen/small_margin"
android:id="@+id/playerView"
android:layout_weight="1" />
<ImageView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="@dimen/small_padding"
android:id="@+id/enemyView"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
GameView.java
package com.example.tubes_2.fragments.surfaceView;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.example.tubes_2.fragments.GameFragment;
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
GameFragment gameFragment;
public GameView(Context ctx) {
super(ctx);
this.setAlpha(0);
this.setZOrderOnTop(true);
this.getHolder().addCallback(this);
}
public GameView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
this.setAlpha(0);
this.setZOrderOnTop(true);
getHolder().addCallback(this);
}
public GameView(Context ctx, GameFragment fragment) {
super(ctx);
this.gameFragment = fragment;
this.setAlpha(0);
this.setZOrderOnTop(true);
this.getHolder().addCallback(this);
}
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
this.gameFragment.initializeGame();
this.gameFragment.startGame();
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
// do nothing atm
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.WHITE, PorterDuff.Mode.CLEAR);
super.onDraw(canvas);
}
}
И, наконец, DrawerThread.java
package com.example.tubes_2.fragments;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.ImageView;
import com.example.tubes_2.R;
import com.example.tubes_2.fragments.surfaceView.GameView;
import com.example.tubes_2.model.Ship;
import com.example.tubes_2.presenter.GameStatus;
public class DrawerThread implements Runnable {
GameFragment game;
GameView gameView;
ImageView playerView, enemyView;
Bitmap playerBitmap, enemyBitmap;
long previousTime, fps;
public DrawerThread(GameFragment game) {
this.previousTime = 0;
this.fps = 60; // PC MASTER RACE, MOBILE PEASANTS!!!
this.game = game;
this.gameView = game.gameView;
this.playerView = game.playerView;
this.enemyView = game.enemyView;
Resources res = game.getResources();
this.playerBitmap = BitmapFactory.decodeResource(res, R.drawable.player_ship);
this.enemyBitmap = BitmapFactory.decodeResource(res, R.drawable.enemy_ship);
}
@Override
public void run() {
GameStatus status = this.game.gameStatus;
SurfaceHolder gameHolder = this.gameView.getHolder();
while (status.getGameState()) {
long currentTimeMillis = System.currentTimeMillis();
long elapsedTimeMs = currentTimeMillis - previousTime;
long sleepTimeMs = (long) (1000f/ fps - elapsedTimeMs);
Canvas gameCanvas = gameHolder.lockCanvas();
try {
if (gameCanvas == null) {
Thread.sleep(1);
continue;
} else if (status.getCountdown() > 0) {
Paint pt = new Paint();
pt.setColor(Color.BLACK);
pt.setTextSize(48);
int xPos = (gameCanvas.getWidth() / 2);
int yPos = (int) ((gameCanvas.getHeight() / 2) - ((pt.descent() + pt.ascent()) / 2));
gameCanvas.drawText(Integer.toString(status.getCountdown()), xPos, yPos, pt);
// just decrement here
status.reduceCountdown();
gameHolder.unlockCanvasAndPost(gameCanvas);
Thread.sleep(1000);
continue;
} else if (sleepTimeMs > 0) {
Thread.sleep(sleepTimeMs);
}
synchronized (gameHolder) {
/*
gameCanvas.drawBitmap(this.playerBitmap, this.game.player.getPositionX(), this.game.player.getPositionY(), null);
gameCanvas.drawBitmap(this.enemyBitmap, this.game.enemy.getPositionX(), this.game.enemy.getPositionY(), null);
*/
}
gameHolder.unlockCanvasAndPost(gameCanvas);
this.previousTime = currentTimeMillis;
} catch (Exception e) {
e.printStackTrace();
}
}
}
private double getPercentageWidth(Ship ship, SurfaceView view) {
return (double)ship.getHealth() / (double)view.getWidth();
}
}
Примечание. Я МОГУ проверить, что поток действительно запущен, я зарегистрировал его.
РЕДАКТИРОВАТЬ: Я МОГУ проверить, что я правильно позвонил GameFragment
, проблема возникает, когда я запускаю DrawerThread
.
Любая помощь будет оценена. Спасибо!