diff --git a/frontend/assets/Roboto-Regular.ttf b/frontend/assets/Roboto-Regular.ttf new file mode 100644 index 0000000000000000000000000000000000000000..67bd9e22486e31cdf67caa1cbdc4c02746af6e33 Binary files /dev/null and b/frontend/assets/Roboto-Regular.ttf differ diff --git a/frontend/assets/badlogic.jpg b/frontend/assets/badlogic.jpg deleted file mode 100644 index 4390da6e0f6d041590c6313d2b4c978abc00a342..0000000000000000000000000000000000000000 Binary files a/frontend/assets/badlogic.jpg and /dev/null differ diff --git a/frontend/assets/main-menu-logout-button.png b/frontend/assets/main-menu-logout-button.png deleted file mode 100644 index a1b72febc88153adae529893666d58927535598e..0000000000000000000000000000000000000000 Binary files a/frontend/assets/main-menu-logout-button.png and /dev/null differ diff --git a/frontend/assets/main-menu-welcome-box.png b/frontend/assets/main-menu-welcome-box.png deleted file mode 100644 index e4d2ba8e49e6c81916a7bab6bc80cd4b4700ec36..0000000000000000000000000000000000000000 Binary files a/frontend/assets/main-menu-welcome-box.png and /dev/null differ diff --git a/frontend/assets/menu-background.png b/frontend/assets/menu-background.png deleted file mode 100644 index bb6d4f3fd9a0723f81c4dbe5e782aab9a953eec4..0000000000000000000000000000000000000000 Binary files a/frontend/assets/menu-background.png and /dev/null differ diff --git a/frontend/assets/menu-button-2.png b/frontend/assets/menu-button-2.png deleted file mode 100644 index 4f9638b61bea3b52dcf7a65a79f5ea9bbc6bc589..0000000000000000000000000000000000000000 Binary files a/frontend/assets/menu-button-2.png and /dev/null differ diff --git a/frontend/assets/menu-button.png b/frontend/assets/menu-button.png deleted file mode 100644 index c0b856f7a2086e059a9a1248dc2fe1c7f9107f3e..0000000000000000000000000000000000000000 Binary files a/frontend/assets/menu-button.png and /dev/null differ diff --git a/frontend/assets/menu-textures.atlas b/frontend/assets/menu-textures.atlas new file mode 100644 index 0000000000000000000000000000000000000000..eefba7954bb4313999cd11a8b3877bd43c77c9f2 --- /dev/null +++ b/frontend/assets/menu-textures.atlas @@ -0,0 +1,75 @@ + +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 diff --git a/frontend/assets/menu-textures.json b/frontend/assets/menu-textures.json new file mode 100644 index 0000000000000000000000000000000000000000..ddbe9ce72cb5b7182dbce2ffdba695da6257277c --- /dev/null +++ b/frontend/assets/menu-textures.json @@ -0,0 +1,46 @@ +{ + "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 diff --git a/frontend/assets/menu-textures.png b/frontend/assets/menu-textures.png new file mode 100644 index 0000000000000000000000000000000000000000..7781c29d8792adc05f5e4f081b3a5828211ed536 Binary files /dev/null and b/frontend/assets/menu-textures.png differ diff --git a/frontend/assets/menu-textures2.png b/frontend/assets/menu-textures2.png new file mode 100644 index 0000000000000000000000000000000000000000..bd24a4c41f8ce767920fca8250da14f197eea118 Binary files /dev/null and b/frontend/assets/menu-textures2.png differ diff --git a/frontend/assets/tankwars-logo.png b/frontend/assets/tankwars-logo.png deleted file mode 100644 index 8ff4dd7f3997f6e7a216b96fa5f5456f78489332..0000000000000000000000000000000000000000 Binary files a/frontend/assets/tankwars-logo.png and /dev/null differ diff --git a/frontend/build.gradle b/frontend/build.gradle index df072c8f6f8609d86179e4af82c732f7c964fc48..b45378c5400ad14ddd3d227d3d8094d071d7f773 100644 --- a/frontend/build.gradle +++ b/frontend/build.gradle @@ -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' } } diff --git a/frontend/core/src/com/game/tankwars/ResourceManager.java b/frontend/core/src/com/game/tankwars/ResourceManager.java new file mode 100644 index 0000000000000000000000000000000000000000..021ebc389503f1de6624a29e835685f46776675e --- /dev/null +++ b/frontend/core/src/com/game/tankwars/ResourceManager.java @@ -0,0 +1,90 @@ +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(); + } + +} diff --git a/frontend/core/src/com/game/tankwars/TankWarsGame.java b/frontend/core/src/com/game/tankwars/TankWarsGame.java index 34d118b95948833eb04257b835120a6e6523ea6a..7ef42679e50284b36e07c8ab0589db9a656c271d 100644 --- a/frontend/core/src/com/game/tankwars/TankWarsGame.java +++ b/frontend/core/src/com/game/tankwars/TankWarsGame.java @@ -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(); } diff --git a/frontend/core/src/com/game/tankwars/controller/LoginController.java b/frontend/core/src/com/game/tankwars/controller/LoginController.java new file mode 100644 index 0000000000000000000000000000000000000000..37830ddaf42c972655d0bada905645d48289f9c5 --- /dev/null +++ b/frontend/core/src/com/game/tankwars/controller/LoginController.java @@ -0,0 +1,85 @@ +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)); + } + +} diff --git a/frontend/core/src/com/game/tankwars/controller/MainMenuController.java b/frontend/core/src/com/game/tankwars/controller/MainMenuController.java index 888b821d8569a0110698ae3eff03ebf56ba37f6c..a655aad55d02a4e0a113690bd46a8ee6ee145602 100644 --- a/frontend/core/src/com/game/tankwars/controller/MainMenuController.java +++ b/frontend/core/src/com/game/tankwars/controller/MainMenuController.java @@ -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; diff --git a/frontend/core/src/com/game/tankwars/model/MenuHeader.java b/frontend/core/src/com/game/tankwars/model/MenuHeader.java new file mode 100644 index 0000000000000000000000000000000000000000..49467013336a61a5ff69bf529232f1683bcc2297 --- /dev/null +++ b/frontend/core/src/com/game/tankwars/model/MenuHeader.java @@ -0,0 +1,17 @@ +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; + } + +} diff --git a/frontend/core/src/com/game/tankwars/view/LoginScreen.java b/frontend/core/src/com/game/tankwars/view/LoginScreen.java new file mode 100644 index 0000000000000000000000000000000000000000..0e5051580e8ebba1aa99ab17243f7a5018a74012 --- /dev/null +++ b/frontend/core/src/com/game/tankwars/view/LoginScreen.java @@ -0,0 +1,138 @@ +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(); + } + + +} diff --git a/frontend/desktop/src/com/game/tankwars/DesktopLauncher.java b/frontend/desktop/src/com/game/tankwars/DesktopLauncher.java index 38508cf87c0dc7e0818462b7a980f63d7e4f7cc3..85cc0a82932dff34c234afc1fea3705d511e58b2 100644 --- a/frontend/desktop/src/com/game/tankwars/DesktopLauncher.java +++ b/frontend/desktop/src/com/game/tankwars/DesktopLauncher.java @@ -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);