Skip to content
Snippets Groups Projects
Commit 33a59ff1 authored by Jan Einar Thorvaldsen's avatar Jan Einar Thorvaldsen
Browse files

Merge branch '19-login-screen-setup-without-backend' into 'main'

Resolve "Login screen setup (without backend)"

Closes #19

See merge request !16
parents 39c8d0a8 fa7d398e
No related branches found
No related tags found
1 merge request!16Resolve "Login screen setup (without backend)"
Pipeline #212998 failed
Showing
with 463 additions and 31 deletions
File added
frontend/assets/badlogic.jpg

66.9 KiB

frontend/assets/main-menu-logout-button.png

508 B

frontend/assets/main-menu-welcome-box.png

462 B

frontend/assets/menu-background.png

9.89 KiB

frontend/assets/menu-button-2.png

356 B

frontend/assets/menu-button.png

504 B

menu-textures.png
size: 1024, 1024
format: RGBA8888
filter: Nearest, Nearest
repeat: none
camo-background-landscape
rotate: false
xy: 2, 530
size: 640, 480
orig: 640, 480
offset: 0, 0
index: -1
camo-background-portrait
rotate: false
xy: 2, 28
size: 224, 500
orig: 224, 500
offset: 0, 0
index: -1
dark-menu-header
rotate: false
xy: 228, 433
size: 629, 95
orig: 629, 95
offset: 0, 0
index: -1
dark-rounded-button
rotate: false
xy: 228, 397
size: 246, 34
orig: 246, 34
offset: 0, 0
index: -1
dark-rounded-button-down
rotate: false
xy: 644, 927
size: 246, 34
orig: 246, 34
offset: 0, 0
index: -1
dark-stroked-input-field
rotate: false
xy: 644, 963
size: 261, 47
orig: 261, 47
offset: 0, 0
index: -1
logo
rotate: false
xy: 228, 355
size: 134, 40
orig: 134, 40
offset: 0, 0
index: -1
typing-cursor
rotate: false
xy: 2, 2
size: 24, 24
orig: 24, 24
offset: 0, 0
index: -1
menu-textures2.png
size: 512, 1024
format: RGBA8888
filter: Nearest, Nearest
repeat: none
camo-background-portrait-blurred
rotate: false
xy: 2, 2
size: 383, 628
orig: 383, 628
offset: 0, 0
index: -1
{
"com.badlogic.gdx.graphics.Color": {
"white": {
"r": 1, "g": 1, "b": 1, "a": 1
},
"black": {
"r": 0, "g": 0, "b": 0, "a": 1
}
},
"BitmapFont": {
"default": {
"file": "Roboto-Regular.ttf",
"size": 18
},
"header": {
"file": "Roboto-Regular.ttf",
"size": 26
}
},
"com.badlogic.gdx.scenes.scene2d.ui.Label$LabelStyle": {
"default": {
"font": "default",
"fontColor": "black"
},
"header": {
"font": "header",
"fontColor": "white"
}
},
"com.badlogic.gdx.scenes.scene2d.ui.TextButton$TextButtonStyle": {
"default": {
"font": "default",
"fontColor": "white",
"up": "dark-rounded-button",
"down": "dark-rounded-button-down"
}
},
"com.badlogic.gdx.scenes.scene2d.ui.TextField$TextFieldStyle": {
"default": {
"font": "default",
"fontColor": "black",
"background": "dark-stroked-input-field",
"cursor": "typing-cursor"
}
}
}
\ No newline at end of file
frontend/assets/menu-textures.png

48.6 KiB

frontend/assets/menu-textures2.png

120 KiB

frontend/assets/tankwars-logo.png

1.1 KiB

......@@ -6,6 +6,7 @@ buildscript {
mavenCentral()
gradlePluginPortal()
maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven { url 'https://jitpack.io' }
google()
}
dependencies {
......@@ -54,8 +55,6 @@ project(":desktop") {
implementation "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop"
}
}
......@@ -96,5 +95,6 @@ project(":core") {
implementation "com.badlogicgames.gdx:gdx-box2d:$gdxVersion"
implementation "com.badlogicgames.gdx:gdx-freetype:$gdxVersion"
implementation 'com.github.acanthite:freetype-skin:0.4'
}
}
package com.game.tankwars;
import com.badlogic.gdx.assets.AssetDescriptor;
import com.badlogic.gdx.assets.AssetManager;
import com.badlogic.gdx.assets.loaders.FileHandleResolver;
import com.badlogic.gdx.assets.loaders.resolvers.InternalFileHandleResolver;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGeneratorLoader;
import com.badlogic.gdx.graphics.g2d.freetype.FreetypeFontLoader;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.github.acanthite.gdx.graphics.g2d.FreeTypeSkinLoader;
/**
* A Singleton that manages all assets in application.
* It uses the LibGDX AssetManager, but encapsulates it to only expose necessary methods.
*
* The application uses scene2d.ui and loads Skin objects, each with a
* TextureAtlas and a JSON file.
*/
public class ResourceManager {
private static ResourceManager instance = null;
private final AssetManager manager;
private final AssetDescriptor<TextureAtlas> MENU_ATLAS =
new AssetDescriptor<>("menu-textures.atlas", TextureAtlas.class);
private final AssetDescriptor<Skin> MENU_SKIN =
new AssetDescriptor<>("menu-textures.json", Skin.class);
public ResourceManager() {
manager = new AssetManager();
manager.setLoader(Skin.class, new FreeTypeSkinLoader(manager.getFileHandleResolver()));
}
/**
* Singleton getter
* @return The ResourceManager instance
*/
public static ResourceManager getInstance() {
if (instance == null) instance = new ResourceManager();
return instance;
}
/**
* Loads the TextureAtlas and the Skin for the menus,
* and attaches the TextureAtlas to the Skin.
*
* @return loaded Skin object with JSON file and TextureRegions
* null on loading error
*/
public Skin loadAndGetMenuAssets() {
if (!manager.isLoaded(MENU_ATLAS)) manager.load(MENU_ATLAS);
if (!manager.isLoaded(MENU_SKIN)) manager.load(MENU_SKIN);
try {
manager.finishLoading();
manager.get(MENU_SKIN).addRegions(manager.get(MENU_ATLAS));
return manager.get(MENU_SKIN);
} catch(GdxRuntimeException error) {
System.out.println(error.getMessage());
}
return null;
}
/**
* Block until all currently loaded assets are finished loading
*/
public void finishLoading() {
manager.finishLoading();
}
/**
* Unload all currently loaded assets
*/
public void clear() {
manager.clear();
}
/**
* Dispose of the manager
*/
public void dispose() {
manager.dispose();
}
}
......@@ -28,6 +28,7 @@ import com.badlogic.gdx.utils.ScreenUtils;
import com.game.tankwars.model.Box2dWorld;
import com.game.tankwars.model.Bullet;
import com.game.tankwars.model.Tank;
import com.game.tankwars.view.LoginScreen;
import com.game.tankwars.view.MainMenuScreen;
public class TankWarsGame extends Game {
......@@ -35,38 +36,14 @@ public class TankWarsGame extends Game {
public static int VIEWPORT_WIDTH = 80;
public static int VIEWPORT_HEIGHT = 50;
private BitmapFont font;
private SpriteBatch batch;
private OrthographicCamera camera;
private BitmapFont font;
@Override
public void create() {
batch = new SpriteBatch();
// Font from https://www.fontspace.com/roll-accurate-font-f32330
font = generateFontFromTTFFile("RollAccurate-mvrx.ttf");
// Camera size set to main menu dimensions: portrait mode
camera = new OrthographicCamera(224f,
224f * Gdx.graphics.getHeight() / Gdx.graphics.getWidth());
MainMenuScreen mainMenuScreen = new MainMenuScreen(this, batch, font, camera);
this.setScreen(mainMenuScreen);
}
/**
* Generates a BitmapFont from a .ttf font file with higher scaling than the original
* to allow for better font resolution.
*
* @param internalPath file path to the .ttf file relative to assets folder
* @return BitmapFont
*/
private BitmapFont generateFontFromTTFFile(String internalPath) {
FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal(internalPath));
FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
parameter.size = 32; // Set max font size
BitmapFont font = generator.generateFont(parameter);
generator.dispose();
return font;
font = new BitmapFont();
this.setScreen(new LoginScreen(this));
}
public int getViewportWidth(){
......@@ -84,6 +61,7 @@ public class TankWarsGame extends Game {
@Override
public void dispose () {
ResourceManager.getInstance().dispose();
batch.dispose();
font.dispose();
}
......
package com.game.tankwars.controller;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.game.tankwars.ResourceManager;
import com.game.tankwars.TankWarsGame;
import com.game.tankwars.view.GameScreen;
/**
* Todo: Login user on backend
*
* Listens to the login button on the LoginScreen and logs in
* the user with the username provided in the username text field.
* Transitions to MainMenuScreen on login.
*/
public class LoginController {
private final TankWarsGame tankWarsGame;
private final Stage stage;
private final TextButton loginButton;
private final TextField usernameField;
public LoginController(final TankWarsGame tankWarsGame, final TextButton loginButton, final TextField usernameField, final Stage stage) {
this.tankWarsGame = tankWarsGame;
this.loginButton = loginButton;
this.usernameField = usernameField;
this.stage = stage;
setEventListeners();
}
public void setEventListeners() {
loginButton.addListener(new InputListener() {
@Override
public boolean touchDown(InputEvent event, float x, float y, int pointer, int button) {
handleInput(usernameField.getText());
return true;
}
});
usernameField.addListener(new InputListener() {
/**
* Unfocus text field and remove keyboard when enter is pressed,
* and move camera back to original position.
*/
@Override
public boolean keyDown(InputEvent event, int keycode) {
super.keyDown(event, keycode);
if (keycode == 66) {
usernameField.getOnscreenKeyboard().show(false);
stage.unfocus(usernameField);
stage.getViewport().setScreenY(0);
stage.getViewport().apply();
}
return true;
}
});
usernameField.addListener(new ClickListener() {
/**
* Move camera down when text field is clicked
* to make the field appear above the keyboard.
*/
@Override
public void clicked(InputEvent event, float x, float y) {
super.clicked(event, x, y);
stage.getViewport().setScreenY((int) (2 * stage.getHeight() / 3));
stage.getViewport().apply();
}
});
}
public void handleInput(String username) {
System.out.println(username);
// TODO: Move clear line to MainMenuController when the main menu is operational
ResourceManager.getInstance().clear();
tankWarsGame.setScreen(new GameScreen(tankWarsGame));
}
}
......@@ -41,7 +41,10 @@ public class MainMenuController {
touchPos.y >= menuButton.getY() && touchPos.y <= menuButton.getY() + menuButton.getHeight()) {
switch (i) {
case 0: tankWarsGame.setScreen(new GameScreen(tankWarsGame)); break;
case 0:
// TODO: Clear resource manager
tankWarsGame.setScreen(new GameScreen(tankWarsGame));
break;
case 1: System.out.println("Leaderboard button: Not yet functional"); break;
case 2: System.out.println("Settings button: Not yet functional"); break;
case 3: System.out.println("Log out button: Not yet functional"); break;
......
package com.game.tankwars.model;
import com.badlogic.gdx.graphics.Texture;
public class MenuHeader {
private final Texture texture;
public MenuHeader() {
texture = new Texture("main-menu-welcome-box.png");
}
public Texture getTexture() {
return texture;
}
}
package com.game.tankwars.view;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.Event;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.InputListener;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
import com.badlogic.gdx.scenes.scene2d.ui.Image;
import com.badlogic.gdx.scenes.scene2d.ui.TextField;
import com.badlogic.gdx.scenes.scene2d.ui.Value;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.Drawable;
import com.badlogic.gdx.scenes.scene2d.utils.FocusListener;
import com.badlogic.gdx.utils.Align;
import com.badlogic.gdx.utils.ScreenUtils;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.game.tankwars.ResourceManager;
import com.game.tankwars.TankWarsGame;
import com.game.tankwars.controller.LoginController;
/**
* Basic login screen with only a username text field.
* The user is logged in with the provided name when the login button
* is touched.
*/
public class LoginScreen implements Screen {
private final TankWarsGame tankWarsGame;
private Stage stage;
private TextField usernameField;
public LoginScreen(final TankWarsGame tankWarsGame) {
this.tankWarsGame = tankWarsGame;
}
@Override
public void show() {
stage = new Stage(new ExtendViewport(320, 240), new SpriteBatch());
Gdx.input.setInputProcessor(stage);
Skin skin = ResourceManager.getInstance().loadAndGetMenuAssets();
// TODO: Transition to ErrorScreen if skin == null
Image backgroundPortrait = new Image(skin.getDrawable("camo-background-portrait"));
Drawable backgroundBlurred = skin.getDrawable("camo-background-portrait-blurred");
Image logo = new Image(skin.getDrawable("logo"));
Drawable headerBox = skin.getDrawable("dark-menu-header");
Label loginLabel = new Label("Log in", skin.get("header", Label.LabelStyle.class));
Label usernameLabel = new Label("Username", skin.get("default", Label.LabelStyle.class));
usernameField = new TextField("",
skin.get("default", TextField.TextFieldStyle.class));
usernameField.setAlignment(Align.center);
TextButton loginButton = new TextButton("Log in",
skin.get("default", TextButton.TextButtonStyle.class));
//--- Layout
float lw = 2 * stage.getWidth() / 5f;
float rw = stage.getWidth() - lw;
Table rootTable = new Table();
rootTable.setFillParent(true);
rootTable.center();
rootTable.add(backgroundPortrait).width(lw).
left().minHeight(stage.getHeight()).
height(backgroundPortrait.getHeight() / backgroundPortrait.getWidth() * lw);
Table rightTable = new Table();
rightTable.background(backgroundBlurred);
Table headerTable = new Table();
headerTable.background(headerBox);
headerTable.add(logo).expandX().width(3 * rw / 7f).
height(logo.getHeight() / logo.getWidth() * 3 * rw / 7f);
headerTable.add(loginLabel).expandX();
rightTable.add(headerTable).fillX().height(stage.getHeight() / 4f);
rightTable.row().expand(1, 1);
rightTable.add(usernameLabel).width(2 * rw / 3f).bottom().padLeft(10);
rightTable.row().expand(1, 0);
rightTable.add(usernameField).width(2 * rw / 3f).height(42).top();
rightTable.row().expand(1, 4);
rightTable.add(loginButton).width(2 * rw / 3f).height(28);
rootTable.add(rightTable).expandX().height(stage.getHeight());
stage.addActor(rootTable);
new LoginController(tankWarsGame, loginButton, usernameField, stage);
}
@Override
public void render(float delta) {
ScreenUtils.clear(0, 0, 0, 1);
stage.act(delta);
stage.draw();
}
@Override
public void resize(int width, int height) {
stage.getViewport().update(width, height);
}
@Override
public void pause() {
}
@Override
public void resume() {
}
@Override
public void hide() {
Gdx.input.setInputProcessor(null);
}
@Override
public void dispose() {
stage.dispose();
}
}
......@@ -8,7 +8,7 @@ import com.game.tankwars.TankWarsGame;
public class DesktopLauncher {
public static void main (String[] arg) {
Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration();
config.setWindowedMode(720, 1600);
config.setWindowedMode(1600, 720);
config.setForegroundFPS(60);
config.setTitle("Tank Wars");
new Lwjgl3Application(new TankWarsGame(), config);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment