I've asked this question before but i am still unclear of how to do this after hours of frustration I have messed up yet another decent project I've been working on. I had a fully functioning Snake game which I was very happy with but I had the idea that I wanted to put the entire game id created onto the screen inside of an image of something similar to an old GameBoy. (in case im not describing this clearly I have made a quick diagram): https://i.stack.imgur.com/3J1Ol.jpg I've read so much on this matter before but I just can't make it work in my own projects.
No matter what I have done (ie. where i set ScreenX position for my inner viewport) the inner viewport always positions at the very left edge of screen and spans the width of screen - even though I have set the "World Width", I want the inner screen inset inside the controllerViewport so that it looks like you are playing it on that device. (I tried using things other than FitViewport, but i need the inner screen to be relative to the VIRTUAL WIDTH, HEIGHT I created.
I will paste the code now (sorry i have it all in one class on this project as it started from a tutorial i completed - i dont usually write my code like this):
public class GameScreen extends ScreenAdapter {
private static final float WORLD_WIDTH = 640;
private static final float WORLD_HEIGHT = 480;
private static final float TOTAL_HEIGHT = 1920 / 2;
private static final float TOTAL_WIDTH = 1080 / 2;
private static final float MOVE_TIME = 0.15f;
private static final int SNAKE_MOVEMENT = 32;
private static final int GRID_CELL = 32;
private static final int RIGHT = 0;
private static final int LEFT = 1;
private static final int UP = 2;
private static final int DOWN = 3;
private static final String GAME_OVER_TEXT = "Game Over... Punk!!";
private static final int POINTS_PER_APPLE = 20;
private SnakeGame game;
private SpriteBatch batch;
private Texture snakeHead;
private Texture snakeBody;
private Texture apple;
private boolean appleAvailable = false;
private int appleX, appleY;
private float timer = MOVE_TIME;
private float snakeX = 0, snakeY = 0;
private float snakeXBeforeUpdate = 0, snakeYBeforeUpdate = 0;
private int snakeDirection = RIGHT;
private Array<BodyPart> bodyParts = new Array<BodyPart>();
private ShapeRenderer shapeRenderer;
private boolean directionSet = false;
private boolean hasHit = false;
private enum STATE { PLAYING, GAME_OVER }
private STATE state = STATE.PLAYING;
private BitmapFont bitmapFont;
private GlyphLayout layout = new GlyphLayout();
private int score = 0;
private Viewport viewport1;
private Camera camera;
private Viewport controllerViewport;
private Dpad dpad;
float dPadWidth;
private Texture dPadTexture;
Vector3 touchPos;
Texture background;
Texture gameboyTexture;
public GameScreen(SnakeGame game){
this.game = game;
batch = new SpriteBatch();
snakeHead = new Texture(Gdx.files.internal("snakehead.png"));
snakeBody = new Texture(Gdx.files.internal("snakebody.png"));
apple = new Texture(Gdx.files.internal("apple.png"));
background = new Texture(Gdx.files.internal("backgroundTile.png"));
dPadTexture = new Texture(Gdx.files.internal("dpad.png"));
gameboyTexture = new Texture("gameboy.png");
shapeRenderer = new ShapeRenderer();
bitmapFont = new BitmapFont();
bitmapFont.getData().setScale(2f,2f); // set font size here
camera = new OrthographicCamera();
camera.position.set(TOTAL_WIDTH / 2, TOTAL_HEIGHT / 2, 0);
camera.update();
controllerViewport = new FillViewport(TOTAL_WIDTH, TOTAL_HEIGHT, camera);
viewport1 = new FitViewport(WORLD_WIDTH, WORLD_HEIGHT);
viewport1.setScreenBounds((int)(controllerViewport.getWorldWidth() * 0.25f), (int)(controllerViewport.getWorldHeight() - (controllerViewport.getWorldHeight() * 0.1f)),
(int)(controllerViewport.getWorldWidth() * 0.75f) , (int)(controllerViewport.getWorldHeight() * 0.33f));
switch (game.dpadSize){
case 1:
dPadWidth = viewport1.getWorldWidth() / 4 * 0.75f;
break;
case 2:
dPadWidth = viewport1.getWorldWidth() / 4 * 1.00f;
break;
case 3:
dPadWidth = viewport1.getWorldWidth() / 4 * 1.25f;
break;
case 4:
dPadWidth = viewport1.getWorldWidth() / 4 * 1.50f;
break;
}
float dPadHeight = dPadWidth;
dpad = new Dpad(dPadTexture, viewport1.getWorldWidth() - dPadWidth, viewport1.getScreenY(), dPadWidth, dPadHeight);
touchPos = new Vector3();
}
@Override
public void show() {
}
@Override
public void render(float delta) {
switch (state) {
case PLAYING:
updateSnake(delta);
queryInput();
checkAppleCollision();
checkAndPlaceApple();
break;
case GAME_OVER:
checkForRestart();
break;
}
clearScreen();
// drawGrid();
draw();
}
@Override
public void resize(int width, int height) {
viewport1.update(width, height);
controllerViewport.update(width, height);
}
private void checkForRestart(){
if (Gdx.input.isKeyPressed(Input.Keys.SPACE)){
doRestart();
}
if (state == STATE.GAME_OVER && Gdx.input.isTouched()){
doRestart();
}
}
private void doRestart() {
state = STATE.PLAYING;
bodyParts.clear();
snakeDirection = RIGHT;
directionSet = false;
timer = MOVE_TIME;
snakeX = 0;
snakeY = 0;
snakeXBeforeUpdate = 0;
snakeYBeforeUpdate = 0;
score = 0;
appleAvailable = false;
}
private void updateSnake(float delta) {
if (!hasHit) {
timer -= delta;
if (timer <= 0) {
timer = MOVE_TIME;
moveSnake();
checkForOutOfBounds();
updateBodyPartsPosition();
checkSnakeBodyCollision();
directionSet = false;
}
}
// add transparency to snake tail:
for (int i = 0; i < bodyParts.size; i++){
bodyParts.get(i).setAlphaValue(0.02f * (i + 6));
if (bodyParts.get(i).alphaValue <= 0.15f){
bodyParts.get(i).alphaValue = 0.15f;
}
if (bodyParts.get(i).alphaValue >= 0.95f){
bodyParts.get(i).alphaValue = 0.95f;
}
}
}
private void updateIfNotOppositeDirection(int newSnakeDirection, int oppositeDirection){
if (snakeDirection != oppositeDirection || bodyParts.size == 0){
snakeDirection = newSnakeDirection;
}
}
private void updateDirection(int newSnakeDirection){
if (!directionSet && snakeDirection != newSnakeDirection){
directionSet = true;
game.step2Sound.play();
switch (newSnakeDirection){
case LEFT:
updateIfNotOppositeDirection(newSnakeDirection, RIGHT);
break;
case RIGHT:
updateIfNotOppositeDirection(newSnakeDirection, LEFT);
break;
case UP:
updateIfNotOppositeDirection(newSnakeDirection, DOWN);
break;
case DOWN:
updateIfNotOppositeDirection(newSnakeDirection, UP);
break;
}
}
}
private void queryInput() {
boolean lPressed = Gdx.input.isKeyPressed(Input.Keys.LEFT);
boolean rPressed = Gdx.input.isKeyPressed(Input.Keys.RIGHT);
boolean uPressed = Gdx.input.isKeyPressed(Input.Keys.UP);
boolean dPressed = Gdx.input.isKeyPressed(Input.Keys.DOWN);
boolean leftDpadPressed = false;
boolean rightDpadPressed = false;
boolean upDpadPressed = false;
boolean downDpadPressed = false;
touchPos.set(Gdx.input.getX(), Gdx.input.getY(), 0);
camera.unproject(touchPos, viewport1.getScreenX(), viewport1.getScreenY(), viewport1.getScreenWidth(), viewport1.getScreenHeight());
if (touchPos.x >= dpad.upRect.x && touchPos.x <= dpad.upRect.x + dpad.upRect.width
&& touchPos.y >= dpad.upRect.y && touchPos.y <= dpad.upRect.y + dpad.upRect.height)
upDpadPressed = true;
if (touchPos.x >= dpad.downRect.x && touchPos.x <= dpad.downRect.x + dpad.downRect.width
&& touchPos.y >= dpad.downRect.y && touchPos.y <= dpad.downRect.y + dpad.downRect.height)
downDpadPressed = true;
if (touchPos.x >= dpad.leftRect.x && touchPos.x <= dpad.leftRect.x + dpad.leftRect.width
&& touchPos.y >= dpad.leftRect.y && touchPos.y <= dpad.leftRect.y + dpad.leftRect.height)
leftDpadPressed = true;
if (touchPos.x >= dpad.rightRect.x && touchPos.x <= dpad.rightRect.x + dpad.rightRect.width
&& touchPos.y >= dpad.rightRect.y && touchPos.y <= dpad.rightRect.y + dpad.rightRect.height)
rightDpadPressed = true;
if (lPressed || leftDpadPressed) updateDirection(LEFT);
if (rPressed || rightDpadPressed) updateDirection(RIGHT);
if (uPressed || upDpadPressed) updateDirection(UP);
if (dPressed || downDpadPressed) updateDirection(DOWN);
}
private void moveSnake() {
snakeXBeforeUpdate = snakeX;
snakeYBeforeUpdate = snakeY;
if (!directionSet) {
game.step1Sound.play(0.1f);
}
switch (snakeDirection) {
case RIGHT: {
snakeX += SNAKE_MOVEMENT;
return;
}
case LEFT: {
snakeX -= SNAKE_MOVEMENT;
return;
}
case UP: {
snakeY += SNAKE_MOVEMENT;
return;
}
case DOWN: {
snakeY -= SNAKE_MOVEMENT;
return;
}
}
}
private void checkForOutOfBounds() {
if (snakeX >= viewport1.getWorldWidth()) {
snakeX = 0;
}
if (snakeX < 0) {
snakeX = viewport1.getWorldWidth() - SNAKE_MOVEMENT;
}
if (snakeY >= viewport1.getScreenY() + viewport1.getWorldHeight()) {
snakeY = viewport1.getScreenY();
}
if (snakeY < viewport1.getScreenY()) {
snakeY = viewport1.getScreenY() + viewport1.getWorldHeight() - SNAKE_MOVEMENT;
}
}
private void updateBodyPartsPosition() {
if (bodyParts.size > 0) {
BodyPart bodyPart = bodyParts.removeIndex(0);
bodyPart.updateBodyPosition(snakeXBeforeUpdate, snakeYBeforeUpdate);
bodyParts.add(bodyPart);
}
}
private void checkAndPlaceApple() {
if (!appleAvailable) {
if (!(appleX == snakeX && appleY == snakeY)) {
if (bodyParts.size == 0) {
appleX = (MathUtils.random((int) (viewport1.getWorldWidth() / SNAKE_MOVEMENT) - 1) * SNAKE_MOVEMENT) + viewport1.getScreenX();
appleY = (MathUtils.random((int) viewport1.getWorldHeight() / SNAKE_MOVEMENT - 1) * SNAKE_MOVEMENT) + viewport1.getScreenY();
appleAvailable = true;
}
for (BodyPart bodyPart : bodyParts) {
if (!(appleX == bodyPart.x && appleY == bodyPart.y)) {
appleX = (MathUtils.random((int) (viewport1.getWorldWidth() / SNAKE_MOVEMENT) - 1) * SNAKE_MOVEMENT) + viewport1.getScreenX();
appleY = (MathUtils.random((int) viewport1.getWorldHeight() / SNAKE_MOVEMENT - 1) * SNAKE_MOVEMENT) + viewport1.getScreenY();
appleAvailable = true;
}
}
}
}
}
private void checkAppleCollision() {
if (appleAvailable && appleX == snakeX && appleY == snakeY) {
BodyPart bodyPart = new BodyPart(snakeBody);
bodyPart.updateBodyPosition(snakeX, snakeY);
bodyParts.insert(0,bodyPart);
score += POINTS_PER_APPLE * bodyParts.size;
game.pickupSound.play(0.7f);
appleAvailable = false;
}
}
private void checkSnakeBodyCollision(){
for (BodyPart bodyPart : bodyParts){
if (bodyPart.x == snakeX && bodyPart.y == snakeY){
state = STATE.GAME_OVER;
game.loseSound.play();
}
}
}
private void clearScreen() {
Gdx.gl.glClearColor(Color.BLACK.r, Color.BLACK.g, Color.BLACK.b, Color.BLACK.a);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
private void draw() {
batch.setProjectionMatrix(camera.combined);
batch.begin();
batch.draw(gameboyTexture, 0, 0, controllerViewport.getWorldWidth(), controllerViewport.getWorldHeight());
batch.draw(background, viewport1.getScreenX(), viewport1.getScreenY(), viewport1.getWorldWidth(), viewport1.getWorldHeight());
batch.draw(snakeHead, snakeX, snakeY);
for (int i = 0; i < bodyParts.size; i++){
bodyParts.get(i).draw(batch);
}
if (appleAvailable) {
batch.draw(apple, appleX, appleY);
}
if (state == STATE.GAME_OVER) {
layout.setText(bitmapFont, GAME_OVER_TEXT);
bitmapFont.draw(batch, GAME_OVER_TEXT, (viewport1.getWorldWidth() - layout.width) / 2, (viewport1.getWorldHeight() - layout.height) / 2);
}
dpad.setColor(1,1,1,0.3f);
dpad.draw(batch);
batch.setColor(1,1,1,1);
drawScore();
batch.end();
}
private void drawScore(){
if (state == STATE.PLAYING){
String scoreAsString = Integer.toString(score);
layout.setText(bitmapFont, scoreAsString);
bitmapFont.draw(batch, scoreAsString, (viewport1.getWorldWidth() - layout.width) / 2, viewport1.getWorldHeight() - layout.height * 2);
}
}
private void drawGrid(){
shapeRenderer.setProjectionMatrix(camera.combined);
shapeRenderer.begin(ShapeRenderer.ShapeType.Line);
for (int x = 0; x < viewport1.getWorldWidth(); x += GRID_CELL){
for (int y = 0; y < viewport1.getWorldHeight(); y += GRID_CELL){
shapeRenderer.rect(x, y, GRID_CELL, GRID_CELL);
}
}
shapeRenderer.end();
}
private class BodyPart {
private float x, y;
private Texture texture;
private float alphaValue;
public BodyPart(Texture texture) {
this.texture = texture;
}
public void updateBodyPosition(float x, float y) {
this.x = x;
this.y = y;
}
public void setAlphaValue(float alpha){
alphaValue = alpha;
}
public void draw(Batch batch) {
if (!(x == snakeX && y == snakeY)){
batch.setColor(1,1,1,alphaValue);
batch.draw(texture, x, y);
batch.setColor(1,1,1,1);
}
}
}
}
If anyone can help me finally figure this out I would be eternally grateful