All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.codingame.gameengine.module.entities.GraphicEntityModule Maven / Gradle / Ivy

package com.codingame.gameengine.module.entities;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import com.codingame.gameengine.core.AbstractPlayer;
import com.codingame.gameengine.core.GameManager;
import com.codingame.gameengine.core.Module;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;

/**
 * 

* The GraphicEntityModule takes care of displaying and animating graphical entities on the replay of the game. *

* Use it by creating shapes, sprites, texts etc, then committing their states to a certain moment of the frame. By default, the states are commited * automatically at the end of the frame. */ @Singleton public class GraphicEntityModule implements Module { //TODO: extra properties for Texts (text wrapping, alignment, ...) static int ENTITY_COUNT = 0; private List newSpriteSheetSplitters; private List> newEntities; private List> entities; private Map worldStates; private World world; private boolean lockWorld; private WorldState currentWorldState; private GameManager gameManager; @Inject private Serializer gameSerializer; @Inject private Provider spriteSheetProvider; @Inject GraphicEntityModule(GameManager gameManager) { this.gameManager = gameManager; world = new World(); entities = new ArrayList<>(); newEntities = new ArrayList<>(); newSpriteSheetSplitters = new ArrayList<>(); lockWorld = false; worldStates = new HashMap<>(); currentWorldState = new WorldState("0"); gameManager.registerModule(this); } /** * Creates a new world data object to be used to compute all frames throughout the game. *

* This method may only be called once. This method may not be called after the frames have started being computed. *

*

* The world's width and height determine how the positions of all entities are mapped onto the viewer. *

* * @param width * The number of units across the width of the visible part of the game. Default is 1920. * @param height * The number of units across the height of the visible part of the game. Default is 1080. * @return the world data object. * @exception IllegalStateException * if the method is called more than once or after the game begins. */ public World createWorld(int width, int height) { if (lockWorld) { throw new IllegalStateException("World creation must be the first use of this module."); } lockWorld = true; world = new World(width, height); return world; } /** * @return the world data currently being used for computing frames */ public World getWorld() { return world; } void loadSpriteSheetSplitter(SpriteSheetSplitter spritesheetsplitter) { newSpriteSheetSplitters.add(spritesheetsplitter); } /** * Create a spritesheet splitter. * * @return a SpriteSheetSplitter */ public SpriteSheetSplitter createSpriteSheetSplitter() { return spriteSheetProvider.get(); } /** *

* Creates a commit for each entity that has had some of its properties modified since a previous commit or the previous frame. *

*

* This means that each one of those entities' graphical counterparts, at instant t of the frame being computed, will have the same properties as * the java object as they are now. *

*

* To force an entity to keep its current state instead of interpolating to the values of next frame, use commitEntityState instead. *

*

* Only the most recent commits are kept for a given t. *

* * * @param t * The instant of the frame 0 ≤ t ≤ 1. * @exception IllegalArgumentException * if the t is not a valid instant. * */ public void commitWorldState(double t) { commitState(t, true, entities.toArray(new Entity[entities.size()])); } /** * This entity's graphical counterpart, at instant t of the frame being computed, will have the same properties as the java object as it is now. *

* Only the most recent commit is kept for a given t. *

* * @param t * The instant of the frame 0 ≤ t ≤ 1. * @param entities * The entity objects to commit. * @exception IllegalArgumentException * if the t is not a valid instant or id entities is empty. * */ public void commitEntityState(double t, Entity... entities) { commitState(t, false, entities); } private void commitState(double t, boolean commitAll, Entity... entities) { requireValidFrameInstant(t); requireNonEmpty(entities); String actualT = Serializer.formatFrameTime(t); WorldState state = worldStates.get(actualT); if (state == null) { state = new WorldState(actualT); worldStates.put(actualT, state); } if (commitAll) { state.markAsWorldCommit(); } flushAllEntityStates(entities, state); } private void flushAllEntityStates(Entity[] entities, WorldState state) { Stream.of(entities).forEach(entity -> state.flushEntityState(entity)); } private void requireNonEmpty(Object[] items) { if (items.length == 0) { throw new IllegalArgumentException("Must not be an empty array"); } } private static void requireValidFrameInstant(double t) { if (t < 0 || t > 1) { throw new IllegalArgumentException("Not a valid frame instant: " + t); } } private void sendFrameData() { autocommit(); Optional load = gameSerializer.serializeLoadSpriteSheets(newSpriteSheetSplitters); newSpriteSheetSplitters.clear(); Optional create = gameSerializer.serializeCreateEntities(newEntities); newEntities.clear(); List orderedStates = worldStates.entrySet().stream() .sorted((e1, e2) -> e1.getValue().getFrameTime().compareTo(e2.getValue().getFrameTime())) .map(Entry::getValue) .collect(Collectors.toList()); List updateBuilder = new ArrayList<>(); for (WorldState nextWorldState : orderedStates) { WorldState worldStateDiff = nextWorldState.diffFromOtherWorldState(currentWorldState); updateBuilder.add(worldStateDiff); currentWorldState.updateAllEntities(nextWorldState); } Optional update = gameSerializer.serializeWorldDiff(updateBuilder); worldStates.clear(); gameManager.setViewData( "entitymodule", Stream.of(load, create, update) .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.joining("\n")) ); } private void autocommit() { WorldState state = worldStates.computeIfAbsent("1", (key) -> new WorldState("1")); state.markAsWorldCommit(); state.flushMissingEntities(entities); } /** * Creates a new Circle entity, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public Circle createCircle() { Circle c = new Circle(); newEntity(c); return c; } /** * Creates a new Sprite entity, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public Sprite createSprite() { Sprite c = new Sprite(); newEntity(c); return c; } /** * Creates a new Line entity, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public Line createLine() { Line c = new Line(); newEntity(c); return c; } /** * Creates a new Rectangle entity, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public Rectangle createRectangle() { Rectangle c = new Rectangle(); newEntity(c); return c; } /** * Creates a new RoundedRectangle entity, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public RoundedRectangle createRoundedRectangle() { RoundedRectangle c = new RoundedRectangle(); newEntity(c); return c; } /** * Creates a new Polygon entity, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public Polygon createPolygon() { Polygon c = new Polygon(); newEntity(c); return c; } /** * Creates a new Text entity, its graphical counterpart will be created on the frame currently being computed. * * @param string * The default string for the text. Can be changed. * @return the entity. Modify its properties to animate the graphical counterpart. */ public Text createText(String string) { Text e = new Text(); e.setText(string); newEntity(e); return e; } /** * Creates a new Text entity, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public Text createText() { Text e = new Text(); newEntity(e); return e; } /** * Creates a new BitmapText entity, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public BitmapText createBitmapText() { BitmapText e = new BitmapText(); newEntity(e); return e; } /** * Creates a new Group entity, its graphical counterpart will be created on the frame currently being computed. *

* A Group represents a collection of other entities. It acts as a container. *

* * @param entities * 0 or more entities to immediately add to this group. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public Group createGroup(Entity... entities) { Group e = new Group(); newEntity(e); e.add(entities); return e; } /** * Creates a new BufferedGroup entity, its graphical counterpart will be created on the frame currently being computed. *

* A BufferedGroup acts like a group but uses its own rendering system to avoid rounding errors when its children are scaled up or down. *

* * @param entities * 0 or more entities to immediately add to this group. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public BufferedGroup createBufferedGroup(Entity... entities) { BufferedGroup e = new BufferedGroup(); newEntity(e); e.add(entities); return e; } /** * Creates a new Sprite animation, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public SpriteAnimation createSpriteAnimation() { SpriteAnimation c = new SpriteAnimation(); newEntity(c); return c; } /** * Creates a new TilingSprite entity, its graphical counterpart will be created on the frame currently being computed. * * @return the entity. Modify its properties to animate the graphical counterpart. */ public TilingSprite createTilingSprite() { TilingSprite c = new TilingSprite(); newEntity(c); return c; } private void newEntity(Entity e) { lockWorld = true; entities.add(e); newEntities.add(e); } private void sendGlobalData() { gameManager.setViewGlobalData("entitymodule", world); lockWorld = true; } @Override public final void onGameInit() { sendGlobalData(); sendFrameData(); } @Override public final void onAfterGameTurn() { sendFrameData(); } @Override public final void onAfterOnEnd() { } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy