Skip to content
Snippets Groups Projects
Commit fa7d398e authored by Sander Østrem Fagernes's avatar Sander Østrem Fagernes Committed by Jan Einar Thorvaldsen
Browse files

Resolve "Login screen setup (without backend)"

parent 39c8d0a8
No related branches found
No related tags found
1 merge request!16Resolve "Login screen setup (without backend)"
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