initial commit
This commit is contained in:
commit
b6920389b0
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
.DS_Store
|
||||
.gradle/
|
||||
out/
|
||||
log/
|
||||
target/
|
||||
build/
|
||||
.settings/
|
||||
.project
|
||||
.classpath
|
||||
.idea
|
||||
*.iml
|
||||
*.eml
|
||||
*.ipr
|
||||
*.iws
|
||||
*.class
|
||||
*.jar
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Gered King
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
43
README.md
Normal file
43
README.md
Normal file
|
@ -0,0 +1,43 @@
|
|||
# Tiles³ Framework: Basic Example
|
||||
|
||||
A basic working example application of the "Tiles³ Framework" in action. This just
|
||||
show cases a complete but very, very simple application that makes use of:
|
||||
|
||||
* [libGDX](http://libgdx.badlogicgames.com/)
|
||||
* [gdx-toolbox](https://github.com/gered/gdx-toolbox)
|
||||
* [gdx-tilemap3d](https://github.com/gered/gdx-tilemap3d)
|
||||
* Basic custom entity/event system and gameplay handling code.
|
||||
|
||||
This basic example is nothing more then a boring walk-around demo. Nothing too
|
||||
exciting as you would expect from something called a "basic example."
|
||||
|
||||
![Screenshot 2](screenshot.png)
|
||||
|
||||
## What is the "Tiles³ Framework"?
|
||||
|
||||
This is what I'm calling my own custom "framework" (and I'm using the word loosely here)
|
||||
for building 3D tile-based top-down real-time action games. This framework is built upon
|
||||
libGDX and my own libraries gdx-toolbox and gdx-tilemap3d and builds on the entity and
|
||||
event system in those with a bunch of pre-built subsystems somewhat suitable for building
|
||||
games.
|
||||
|
||||
**This is NOT intended to be a generic game engine / library.** Rather, it is just my
|
||||
common game template (for these specific types of games) which I'm giving a name
|
||||
and putting up for anyone else to use. You will _very likely_ want to heavily
|
||||
customize this, assuming it even meets your needs at all!
|
||||
|
||||
## Running
|
||||
|
||||
You will need Gradle. Clone the repository and then from a terminal:
|
||||
|
||||
$ gradle desktop:run
|
||||
|
||||
And it should download the project dependencies, build and then run.
|
||||
|
||||
## TODO
|
||||
|
||||
[[Write something here briefly explaining the project and code structure]]
|
||||
|
||||
## License
|
||||
|
||||
Distributed under the the MIT License. See LICENSE for more details.
|
101
assets/consoleFont.fnt
Normal file
101
assets/consoleFont.fnt
Normal file
|
@ -0,0 +1,101 @@
|
|||
info face="DeluxeFontReg" size=8 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0
|
||||
common lineHeight=9 base=7 scaleW=512 scaleH=512 pages=1 packed=0
|
||||
page id=0 file="consoleFont.png"
|
||||
chars count=95
|
||||
char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=7 xadvance=8 page=0 chnl=0
|
||||
char id=106 x=0 y=0 width=6 height=9 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=38 x=6 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=35 x=14 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=36 x=22 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=92 x=29 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=64 x=37 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=47 x=45 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=124 x=53 y=0 width=3 height=8 xoffset=3 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=62 x=56 y=0 width=6 height=8 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=60 x=62 y=0 width=6 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=125 x=68 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=123 x=75 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=93 x=82 y=0 width=5 height=8 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=91 x=87 y=0 width=5 height=8 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=41 x=92 y=0 width=5 height=8 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=40 x=97 y=0 width=5 height=8 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=59 x=102 y=0 width=4 height=8 xoffset=1 yoffset=1 xadvance=8 page=0 chnl=0
|
||||
char id=63 x=106 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=33 x=113 y=0 width=3 height=8 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=48 x=116 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=57 x=124 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=56 x=131 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=55 x=138 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=54 x=145 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=53 x=152 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=52 x=159 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=51 x=167 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=50 x=174 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=49 x=181 y=0 width=5 height=8 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=116 x=186 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=108 x=193 y=0 width=4 height=8 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=107 x=197 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=105 x=204 y=0 width=3 height=8 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=104 x=207 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=102 x=214 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=100 x=221 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=98 x=228 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=90 x=235 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=89 x=243 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=88 x=250 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=87 x=258 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=86 x=266 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=85 x=273 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=84 x=280 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=83 x=287 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=82 x=294 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=81 x=301 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=80 x=308 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=79 x=315 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=78 x=322 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=77 x=330 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=76 x=338 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=75 x=345 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=74 x=353 y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=73 x=361 y=0 width=5 height=8 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=72 x=366 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=71 x=373 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=70 x=380 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=69 x=387 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=68 x=394 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=67 x=401 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=66 x=408 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=65 x=415 y=0 width=7 height=8 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=127 x=422 y=0 width=8 height=7 xoffset=0 yoffset=1 xadvance=8 page=0 chnl=0
|
||||
char id=37 x=430 y=0 width=8 height=7 xoffset=0 yoffset=1 xadvance=8 page=0 chnl=0
|
||||
char id=58 x=438 y=0 width=3 height=7 xoffset=2 yoffset=1 xadvance=8 page=0 chnl=0
|
||||
char id=121 x=441 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=113 x=448 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=112 x=455 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=103 x=462 y=0 width=7 height=7 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=42 x=469 y=0 width=9 height=6 xoffset=0 yoffset=1 xadvance=8 page=0 chnl=0
|
||||
char id=43 x=478 y=0 width=7 height=6 xoffset=0 yoffset=1 xadvance=8 page=0 chnl=0
|
||||
char id=122 x=485 y=0 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=120 x=492 y=0 width=8 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=119 x=500 y=0 width=8 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=118 x=0 y=9 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=117 x=7 y=9 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=115 x=14 y=9 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=114 x=21 y=9 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=111 x=28 y=9 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=110 x=35 y=9 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=109 x=42 y=9 width=8 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=101 x=50 y=9 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=99 x=57 y=9 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=97 x=64 y=9 width=7 height=6 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=61 x=71 y=9 width=7 height=5 xoffset=0 yoffset=2 xadvance=8 page=0 chnl=0
|
||||
char id=94 x=78 y=9 width=8 height=5 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=44 x=86 y=9 width=4 height=4 xoffset=1 yoffset=5 xadvance=8 page=0 chnl=0
|
||||
char id=39 x=90 y=9 width=4 height=4 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=96 x=94 y=9 width=4 height=4 xoffset=2 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=34 x=98 y=9 width=6 height=4 xoffset=1 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=126 x=104 y=9 width=8 height=3 xoffset=0 yoffset=0 xadvance=8 page=0 chnl=0
|
||||
char id=46 x=112 y=9 width=3 height=3 xoffset=2 yoffset=5 xadvance=8 page=0 chnl=0
|
||||
char id=95 x=115 y=9 width=9 height=2 xoffset=0 yoffset=7 xadvance=8 page=0 chnl=0
|
||||
char id=45 x=124 y=9 width=7 height=2 xoffset=0 yoffset=3 xadvance=8 page=0 chnl=0
|
||||
kernings count=-1
|
BIN
assets/consoleFont.png
Normal file
BIN
assets/consoleFont.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
6
assets/player.atlas.json
Normal file
6
assets/player.atlas.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"texture": "player.png",
|
||||
"tiles": [
|
||||
{"autogrid": true, "numTilesX": 8, "numTilesY": 8, "tileWidth": 16, "tileHeight": 16},
|
||||
]
|
||||
}
|
BIN
assets/player.png
Normal file
BIN
assets/player.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5 KiB |
15
assets/readme.txt
Normal file
15
assets/readme.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
Artwork Credits
|
||||
---------------
|
||||
|
||||
Player sprite:
|
||||
Oryx and Geese (animations)
|
||||
http://forums.tigsource.com/index.php?topic=8970.0
|
||||
|
||||
Font:
|
||||
DeluxeFont by codeman38
|
||||
http://www.zone38.net/font/#deluxefont
|
||||
|
||||
Terrain Textures:
|
||||
From an 8x8 Minecraft Texturepack that I can't find anymore. There are
|
||||
several of them and none of the ones I checked, at the time I was
|
||||
writing these credits, exactly match the textures I copied. :(
|
7
assets/tiles/bricks.tilemesh
Normal file
7
assets/tiles/bricks.tilemesh
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"cube": true,
|
||||
"texture": 7,
|
||||
"faces": ["ALL"],
|
||||
"opaqueSides": ["ALL"]
|
||||
}
|
7
assets/tiles/dirt.tilemesh
Normal file
7
assets/tiles/dirt.tilemesh
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"cube": true,
|
||||
"texture": 2,
|
||||
"faces": ["ALL"],
|
||||
"opaqueSides": ["ALL"]
|
||||
}
|
14
assets/tiles/dirt_grass_top.tilemesh
Normal file
14
assets/tiles/dirt_grass_top.tilemesh
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"cube": true,
|
||||
"textures": {
|
||||
"top": 0,
|
||||
"left": 1,
|
||||
"front": 1,
|
||||
"right": 1,
|
||||
"back": 1,
|
||||
"bottom": 2
|
||||
},
|
||||
"faces": ["ALL"],
|
||||
"opaqueSides": ["ALL"]
|
||||
}
|
7
assets/tiles/grass.tilemesh
Normal file
7
assets/tiles/grass.tilemesh
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"cube": true,
|
||||
"texture": 0,
|
||||
"faces": ["ALL"],
|
||||
"opaqueSides": ["ALL"]
|
||||
}
|
8
assets/tiles/leaves.tilemesh
Normal file
8
assets/tiles/leaves.tilemesh
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"cube": true,
|
||||
"texture": 3,
|
||||
"faces": ["ALL"],
|
||||
"alpha": true,
|
||||
"translucency": 0.25,
|
||||
}
|
7
assets/tiles/planks.tilemesh
Normal file
7
assets/tiles/planks.tilemesh
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"cube": true,
|
||||
"texture": 6,
|
||||
"faces": ["ALL"],
|
||||
"opaqueSides": ["ALL"]
|
||||
}
|
7
assets/tiles/sand.tilemesh
Normal file
7
assets/tiles/sand.tilemesh
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"cube": true,
|
||||
"texture": 9,
|
||||
"faces": ["ALL"],
|
||||
"opaqueSides": ["ALL"]
|
||||
}
|
7
assets/tiles/stone_floor.tilemesh
Normal file
7
assets/tiles/stone_floor.tilemesh
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"cube": true,
|
||||
"texture": 8,
|
||||
"faces": ["ALL"],
|
||||
"opaqueSides": ["ALL"]
|
||||
}
|
6
assets/tiles/terrain.atlas.json
Normal file
6
assets/tiles/terrain.atlas.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"texture": "terrain.png",
|
||||
"tiles": [
|
||||
{"autogrid": true, "numTilesX": 8, "numTilesY": 8, "tileWidth": 8, "tileHeight": 8},
|
||||
]
|
||||
}
|
BIN
assets/tiles/terrain.png
Normal file
BIN
assets/tiles/terrain.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.1 KiB |
14
assets/tiles/tiles.json
Normal file
14
assets/tiles/tiles.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"tiles": [
|
||||
"grass.tilemesh",
|
||||
"dirt_grass_top.tilemesh",
|
||||
"dirt.tilemesh",
|
||||
"leaves.tilemesh",
|
||||
"trunk.tilemesh",
|
||||
"planks.tilemesh",
|
||||
"bricks.tilemesh",
|
||||
"stone_floor.tilemesh",
|
||||
"sand.tilemesh"
|
||||
]
|
||||
}
|
14
assets/tiles/trunk.tilemesh
Normal file
14
assets/tiles/trunk.tilemesh
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"textureAtlas": "terrain.atlas.json",
|
||||
"cube": true,
|
||||
"textures": {
|
||||
"top": 4,
|
||||
"left": 5,
|
||||
"front": 5,
|
||||
"right": 5,
|
||||
"back": 5,
|
||||
"bottom": 4
|
||||
},
|
||||
"faces": ["ALL"],
|
||||
"opaqueSides": ["ALL"]
|
||||
}
|
40
build.gradle
Normal file
40
build.gradle
Normal file
|
@ -0,0 +1,40 @@
|
|||
buildscript {
|
||||
repositories { mavenCentral() }
|
||||
}
|
||||
|
||||
allprojects {
|
||||
apply plugin: "eclipse"
|
||||
apply plugin: "idea"
|
||||
|
||||
version = "0.1"
|
||||
ext.appName = "Tiles³ Basic Example"
|
||||
ext.gdxVersion = "1.0.0"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven { url "http://oss.sonatype.org/content/repositories/releases/" }
|
||||
maven { url "http://maven.blarg.ca" }
|
||||
mavenLocal();
|
||||
}
|
||||
}
|
||||
|
||||
project(":core") {
|
||||
apply plugin: "java"
|
||||
|
||||
dependencies {
|
||||
compile "com.badlogicgames.gdx:gdx:$gdxVersion"
|
||||
compile "ca.blarg.gdx:gdx-toolbox:0.1-SNAPSHOT"
|
||||
compile "ca.blarg.gdx:gdx-tilemap3d:0.1-SNAPSHOT"
|
||||
}
|
||||
}
|
||||
|
||||
project(":desktop") {
|
||||
apply plugin: "java"
|
||||
|
||||
dependencies {
|
||||
compile project(":core")
|
||||
compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion"
|
||||
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
||||
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
||||
}
|
||||
}
|
9
core/build.gradle
Normal file
9
core/build.gradle
Normal file
|
@ -0,0 +1,9 @@
|
|||
apply plugin: "java"
|
||||
|
||||
sourceCompatibility = 1.6
|
||||
|
||||
sourceSets.main.java.srcDirs = [ "src/main/java/" ]
|
||||
|
||||
eclipse.project {
|
||||
name = appName + "-core"
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package ca.blarg.tiles3.basicexample;
|
||||
|
||||
import ca.blarg.gdx.GameApp;
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.tilemap3d.assets.TileAssetUtils;
|
||||
import ca.blarg.gdx.tilemap3d.tilemesh.CollisionShapes;
|
||||
import com.badlogic.gdx.Application;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.InputMultiplexer;
|
||||
import com.badlogic.gdx.assets.AssetManager;
|
||||
|
||||
public class BasicExampleGameApp extends GameApp {
|
||||
public BasicExampleGameApp() {
|
||||
toggleHeapMemUsageLogging(false);
|
||||
Gdx.app.setLogLevel(Application.LOG_DEBUG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
TileAssetUtils.registerLoaders(Services.get(AssetManager.class));
|
||||
CollisionShapes.init();
|
||||
|
||||
InputMultiplexer inputMultiplexer = new InputMultiplexer();
|
||||
ContentCache contentCache = new ContentCache();
|
||||
|
||||
Services.register(this);
|
||||
Services.register(contentCache);
|
||||
Services.register(inputMultiplexer);
|
||||
|
||||
Gdx.input.setInputProcessor(inputMultiplexer);
|
||||
Gdx.input.setCatchBackKey(true);
|
||||
Gdx.input.setCatchMenuKey(true);
|
||||
|
||||
stateManager.push(GamePlayState.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
Gdx.input.setInputProcessor(null);
|
||||
|
||||
Services.unregisterAll();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package ca.blarg.tiles3.basicexample;
|
||||
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.graphics.atlas.TextureAtlas;
|
||||
import ca.blarg.gdx.graphics.atlas.TextureAtlasAnimator;
|
||||
import ca.blarg.gdx.tilemap3d.tilemesh.TileMeshCollection;
|
||||
import com.badlogic.gdx.assets.AssetManager;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
|
||||
public class ContentCache implements Services.Service {
|
||||
public BitmapFont font;
|
||||
|
||||
public TileMeshCollection tiles;
|
||||
public TextureAtlas terrain;
|
||||
public TextureAtlasAnimator terrainAnimator = new TextureAtlasAnimator();
|
||||
|
||||
public TextureAtlas player;
|
||||
|
||||
@Override
|
||||
public void onRegister() {
|
||||
AssetManager assetManager = Services.get(AssetManager.class);
|
||||
|
||||
assetManager.load("consoleFont.fnt", BitmapFont.class);
|
||||
|
||||
assetManager.load("tiles/terrain.atlas.json", TextureAtlas.class);
|
||||
assetManager.load("player.atlas.json", TextureAtlas.class);
|
||||
assetManager.load("tiles/tiles.json", TileMeshCollection.class);
|
||||
|
||||
// normally we would probably want to load asynchronously and show a loading bar of sorts
|
||||
assetManager.finishLoading();
|
||||
|
||||
font = assetManager.get("consoleFont.fnt");
|
||||
terrain = assetManager.get("tiles/terrain.atlas.json");
|
||||
tiles = assetManager.get("tiles/tiles.json");
|
||||
player = assetManager.get("player.atlas.json");
|
||||
|
||||
terrainAnimator.addAllAtlases(assetManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUnregister() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package ca.blarg.tiles3.basicexample;
|
||||
|
||||
import ca.blarg.gdx.GameLooper;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.PlayerComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.PhysicsComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.PositionComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.rendering.AnimationComponent;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.utils.StringBuilder;
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.gdx.graphics.ExtendedSpriteBatch;
|
||||
import ca.blarg.gdx.graphics.ViewportContext;
|
||||
import ca.blarg.gdx.processes.GameProcess;
|
||||
import ca.blarg.gdx.processes.ProcessManager;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.StateComponent;
|
||||
|
||||
public class DebugInfoProcess extends GameProcess {
|
||||
StringBuilder sb = new StringBuilder(1024);
|
||||
|
||||
final ViewportContext viewportContext;
|
||||
final ExtendedSpriteBatch spriteBatch;
|
||||
final EntityManager entityManager;
|
||||
final ContentCache content;
|
||||
|
||||
public DebugInfoProcess(ProcessManager processManager, EventManager eventManager) {
|
||||
super(processManager, eventManager);
|
||||
|
||||
viewportContext = Services.get(ViewportContext.class);
|
||||
spriteBatch = Services.get(ExtendedSpriteBatch.class);
|
||||
entityManager = Services.get(EntityManager.class);
|
||||
content = Services.get(ContentCache.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRender(float interpolation) {
|
||||
sb.length = 0;
|
||||
|
||||
GameLooper looper = Services.get(GameLooper.class);
|
||||
|
||||
sb.append("FPS: ").append(Gdx.graphics.getFramesPerSecond()).append(", UD: ").append(looper.getUpdateDelta())
|
||||
.append(", PS: ").append(viewportContext.getOrthographicViewport().getUnitsPerPixel())
|
||||
.append(" (").append((int)viewportContext.getOrthographicViewport().getWorldWidth()).append('x').append((int)viewportContext.getOrthographicViewport().getWorldHeight()).append(")").append('\n');
|
||||
sb.append("CP: ").append(viewportContext.getPerspectiveCamera().position.x).append(',')
|
||||
.append(viewportContext.getPerspectiveCamera().position.y).append(',')
|
||||
.append(viewportContext.getPerspectiveCamera().position.z).append('\n');
|
||||
|
||||
if (entityManager != null) {
|
||||
Entity player = entityManager.getFirstWith(PlayerComponent.class);
|
||||
if (player != null)
|
||||
showEntityInfo(player);
|
||||
}
|
||||
|
||||
spriteBatch.begin();
|
||||
spriteBatch.draw(content.font, 5, viewportContext.getOrthographicViewport().getWorldHeight() - 5, sb, viewportContext.getOrthographicViewport().getUnitsPerPixel());
|
||||
spriteBatch.end();
|
||||
}
|
||||
|
||||
private void showEntityInfo(Entity entity) {
|
||||
PositionComponent position = entity.get(PositionComponent.class);
|
||||
StateComponent state = entity.get(StateComponent.class);
|
||||
AnimationComponent animation = entity.get(AnimationComponent.class);
|
||||
PhysicsComponent physics = entity.get(PhysicsComponent.class);
|
||||
|
||||
sb.append("EP: ").append(position.position.x).append(',').append(position.position.y).append(',').append(position.position.z).append('\n');
|
||||
|
||||
sb.append("EV: ").append(physics.velocity.x).append(',').append(physics.velocity.y).append(',').append(physics.velocity.z).append('\n');
|
||||
sb.append("C: ").append(physics.sweptSphere.foundCollision)
|
||||
.append(" CD: ").append(physics.sweptSphere.nearestCollisionDistance)
|
||||
.append(" S: ").append(physics.isSliding)
|
||||
.append(" SA: ").append(MathUtils.radDeg * (float)Math.acos(physics.sweptSphere.slidingPlaneNormal.dot(Vector3.Y))).append('\n');
|
||||
sb.append("OG: ").append(physics.isOnGround)
|
||||
.append(" W: ").append(physics.isWalking).append(" WW: ").append(physics.wasWalking)
|
||||
.append(" M: ").append(physics.isInMotion).append(" WM: ").append(physics.wasInMotion)
|
||||
.append(" F: ").append(physics.isFalling).append(" WF: ").append(physics.wasFalling).append('\n');
|
||||
sb.append("CP: ").append(physics.nearestIntersection.x).append(',')
|
||||
.append(physics.nearestIntersection.y).append(',')
|
||||
.append(physics.nearestIntersection.z).append('\n');
|
||||
|
||||
sb.append("ES: ").append(state.state).append(" (").append(state.isInActionState).append(" - ").append(state.actionState).append(')').append('\n');
|
||||
|
||||
sb.append("AS: ").append(animation.currentSequenceName)
|
||||
.append(" (T: ").append(animation.frameTime).append(", CF: ").append(animation.currentFrame).append(", NF: ").append(animation.nextFrame)
|
||||
.append(" (FS: ").append(animation.currentSequence.start).append(", FE: ").append(animation.currentSequence.stop).append(")")
|
||||
.append(" L: ").append(animation.isLooping).append(", UO: ").append(animation.currentSequenceUnoverrideable).append(")")
|
||||
.append(" A: ").append(animation.isAnimating()).append(", F: ").append(animation.isAnimationFinished()).append('\n');
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
package ca.blarg.tiles3.basicexample;
|
||||
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.gdx.graphics.EulerPerspectiveCamera;
|
||||
import ca.blarg.gdx.graphics.GraphicsHelpers;
|
||||
import ca.blarg.gdx.graphics.ViewportContext;
|
||||
import ca.blarg.gdx.graphics.screeneffects.DimScreenEffect;
|
||||
import ca.blarg.gdx.graphics.screeneffects.ScreenEffectHelpers;
|
||||
import ca.blarg.gdx.math.MathHelpers;
|
||||
import ca.blarg.gdx.states.GameState;
|
||||
import ca.blarg.gdx.states.StateManager;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.PlayerComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.WorldComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.events.JumpEvent;
|
||||
import ca.blarg.tiles3.basicexample.entities.events.MoveAndTurnEvent;
|
||||
import ca.blarg.tiles3.basicexample.entities.presets.PlayerPreset;
|
||||
import ca.blarg.tiles3.basicexample.entities.subsystems.*;
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.graphics.g3d.ModelBatch;
|
||||
import com.badlogic.gdx.math.Vector2;
|
||||
|
||||
public class GamePlayState extends GameState {
|
||||
final ViewportContext viewportContext;
|
||||
final ModelBatch modelBatch;
|
||||
final ContentCache content;
|
||||
|
||||
EulerPerspectiveCamera camera;
|
||||
EntityManager entityManager;
|
||||
|
||||
public GamePlayState(StateManager stateManager, EventManager eventManager) {
|
||||
super(stateManager, eventManager);
|
||||
viewportContext = Services.get(ViewportContext.class);
|
||||
modelBatch = Services.get(ModelBatch.class);
|
||||
content = Services.get(ContentCache.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPush() {
|
||||
camera = new EulerPerspectiveCamera(60.0f, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
|
||||
camera.moveTo(12.0f, 10.0f, 16.0f);
|
||||
camera.pitch(35.0f);
|
||||
gameApp.viewportContext.setPerspectiveCamera(camera);
|
||||
|
||||
entityManager = new EntityManager(eventManager);
|
||||
Services.register(entityManager);
|
||||
|
||||
initEntitySystem();
|
||||
|
||||
// world entity
|
||||
Entity worldEntity = entityManager.add();
|
||||
WorldComponent world = worldEntity.add(WorldComponent.class);
|
||||
world.tileMap = Level.create();
|
||||
world.renderGrid = false;
|
||||
world.renderSkybox = false;
|
||||
|
||||
entityManager.addUsingPreset(PlayerPreset.class);
|
||||
|
||||
processManager.add(DebugInfoProcess.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPop() {
|
||||
viewportContext.setDefaultPerspectiveCamera();
|
||||
Services.unregister(EntityManager.class);
|
||||
entityManager.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRender(float interpolation) {
|
||||
GraphicsHelpers.clear(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
|
||||
entityManager.onRender(interpolation);
|
||||
super.onRender(interpolation);
|
||||
}
|
||||
|
||||
final static Vector2 moveDirection = new Vector2();
|
||||
@Override
|
||||
public void onUpdateGameState(float delta) {
|
||||
super.onUpdateGameState(delta);
|
||||
if (isTransitioning() || !isTopState())
|
||||
return;
|
||||
|
||||
if (Gdx.input.isKeyPressed(Input.Keys.ESCAPE) || Gdx.input.isKeyPressed(Input.Keys.BACK))
|
||||
setFinished();
|
||||
|
||||
Entity player = entityManager.getFirstWith(PlayerComponent.class);
|
||||
|
||||
if (player != null) {
|
||||
float referenceAngle = ((EulerPerspectiveCamera)gameApp.viewportContext.getPerspectiveCamera()).getYaw();
|
||||
|
||||
moveDirection.set(Vector2.Zero);
|
||||
if (Gdx.input.isKeyPressed(Input.Keys.W) || Gdx.input.isKeyPressed(Input.Keys.UP))
|
||||
moveDirection.y -= 1.0f;
|
||||
if (Gdx.input.isKeyPressed(Input.Keys.S) || Gdx.input.isKeyPressed(Input.Keys.DOWN))
|
||||
moveDirection.y += 1.0f;
|
||||
if (Gdx.input.isKeyPressed(Input.Keys.A) || Gdx.input.isKeyPressed(Input.Keys.LEFT))
|
||||
moveDirection.x -= 1.0f;
|
||||
if (Gdx.input.isKeyPressed(Input.Keys.D) || Gdx.input.isKeyPressed(Input.Keys.RIGHT))
|
||||
moveDirection.x += 1.0f;
|
||||
|
||||
if (moveDirection.len2() != 0.0f) {
|
||||
moveDirection.nor();
|
||||
moveDirection.rotate(referenceAngle);
|
||||
|
||||
MoveAndTurnEvent moveEvent = eventManager.create(MoveAndTurnEvent.class);
|
||||
moveEvent.entity = player;
|
||||
moveEvent.angle = MathHelpers.getAngleFromPointOnCircle(moveDirection.x, moveDirection.y);
|
||||
eventManager.queue(moveEvent);
|
||||
}
|
||||
|
||||
if (Gdx.input.isKeyPressed(Input.Keys.SPACE)) {
|
||||
JumpEvent jumpEvent = eventManager.create(JumpEvent.class);
|
||||
jumpEvent.entity = player;
|
||||
eventManager.queue(jumpEvent);
|
||||
}
|
||||
}
|
||||
|
||||
entityManager.onUpdateGameState(delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateFrame(float delta) {
|
||||
super.onUpdateFrame(delta);
|
||||
if (isTransitioning() || !isTopState())
|
||||
return;
|
||||
|
||||
content.terrainAnimator.onUpdate(delta);
|
||||
entityManager.onUpdateFrame(delta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppPause() {
|
||||
super.onAppPause();
|
||||
entityManager.onAppPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAppResume() {
|
||||
super.onAppResume();
|
||||
entityManager.onAppResume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause(boolean dueToOverlay) {
|
||||
super.onPause(dueToOverlay);
|
||||
if (dueToOverlay)
|
||||
effectManager.add(DimScreenEffect.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume(boolean fromOverlay) {
|
||||
super.onResume(fromOverlay);
|
||||
if (fromOverlay)
|
||||
effectManager.remove(DimScreenEffect.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTransition(float delta, boolean isTransitioningOut, boolean started) {
|
||||
return ScreenEffectHelpers.doFadingTransition(effectManager, isTransitioningOut, started);
|
||||
}
|
||||
|
||||
private void initEntitySystem() {
|
||||
entityManager.addSubsystem(PreviousPositionSystem.class);
|
||||
entityManager.addSubsystem(PhysicsSystem.class);
|
||||
entityManager.addSubsystem(BoundingVolumeWorldPositioningSystem.class);
|
||||
entityManager.addSubsystem(EntityStateSystem.class);
|
||||
entityManager.addSubsystem(CameraSystem.class);
|
||||
entityManager.addSubsystem(AnimationSystem.class);
|
||||
entityManager.addSubsystem(WorldRendererSystem.class);
|
||||
entityManager.addSubsystem(BillboardRenderSystem.class);
|
||||
|
||||
entityManager.addPreset(PlayerPreset.class);
|
||||
}
|
||||
}
|
66
core/src/main/java/ca/blarg/tiles3/basicexample/Level.java
Normal file
66
core/src/main/java/ca/blarg/tiles3/basicexample/Level.java
Normal file
|
@ -0,0 +1,66 @@
|
|||
package ca.blarg.tiles3.basicexample;
|
||||
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.tilemap3d.Tile;
|
||||
import ca.blarg.gdx.tilemap3d.TileMap;
|
||||
import ca.blarg.gdx.tilemap3d.lighting.LightSpreadingTileMapLighter;
|
||||
import ca.blarg.gdx.tilemap3d.lighting.LitChunkVertexGenerator;
|
||||
import ca.blarg.gdx.tilemap3d.prefabs.TilePrefab;
|
||||
|
||||
public class Level {
|
||||
public static TileMap create() {
|
||||
ContentCache content = Services.get(ContentCache.class);
|
||||
|
||||
TileMap map = new TileMap(
|
||||
16, 16, 16,
|
||||
2, 1, 2,
|
||||
content.tiles,
|
||||
new LitChunkVertexGenerator(),
|
||||
new LightSpreadingTileMapLighter());
|
||||
|
||||
/*
|
||||
0 empty
|
||||
1 grass
|
||||
2 grass+dirt
|
||||
3 dirt
|
||||
4 leaves
|
||||
5 trunk
|
||||
6 planks
|
||||
7 stone brick
|
||||
8 stone floor tile
|
||||
9 sand
|
||||
*/
|
||||
|
||||
for (int x = 0; x < map.getWidth(); ++x) {
|
||||
for (int z = 0; z < map.getDepth(); ++z) {
|
||||
if (x == 0 || z == 0 || x == map.getWidth() - 1 || z == map.getDepth() - 1) {
|
||||
map.get(x, 0, z).set(7, Tile.FLAG_COLLIDEABLE);
|
||||
map.get(x, 1, z).set(7, Tile.FLAG_COLLIDEABLE);
|
||||
map.get(x, 2, z).set(7, Tile.FLAG_COLLIDEABLE);
|
||||
} else
|
||||
map.get(x, 0, z).set(2, Tile.FLAG_COLLIDEABLE);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 10; x < map.getWidth() - 10; x += 7) {
|
||||
Prefabs.tree.placeIn(map, x, 1, 3, TilePrefab.Rotation.ROT0, false);
|
||||
Prefabs.tree.placeIn(map, x, 1, map.getDepth() - 3 - Prefabs.tree.getDepth(), TilePrefab.Rotation.ROT0, false);
|
||||
}
|
||||
|
||||
for (int z = 10; z < map.getWidth() - 10; z += 7) {
|
||||
Prefabs.tree.placeIn(map, 3, 1, z, TilePrefab.Rotation.ROT0, false);
|
||||
Prefabs.tree.placeIn(map, map.getWidth() - 3 - Prefabs.tree.getWidth(), 1, z, TilePrefab.Rotation.ROT0, false);
|
||||
}
|
||||
|
||||
Prefabs.tree.placeIn(map, 4, 1, 4, TilePrefab.Rotation.ROT0, false);
|
||||
Prefabs.tree.placeIn(map, map.getWidth() - 4 - Prefabs.tree.getWidth(), 1, 4, TilePrefab.Rotation.ROT0, false);
|
||||
Prefabs.tree.placeIn(map, 4, 1, map.getDepth() - 4 - Prefabs.tree.getDepth(), TilePrefab.Rotation.ROT0, false);
|
||||
Prefabs.tree.placeIn(map, map.getWidth() - 4 - Prefabs.tree.getWidth(), 1, map.getDepth() - 4 - Prefabs.tree.getDepth(), TilePrefab.Rotation.ROT0, false);
|
||||
|
||||
map.updateLighting();
|
||||
map.updateVertices();
|
||||
|
||||
return map;
|
||||
}
|
||||
}
|
38
core/src/main/java/ca/blarg/tiles3/basicexample/Prefabs.java
Normal file
38
core/src/main/java/ca/blarg/tiles3/basicexample/Prefabs.java
Normal file
|
@ -0,0 +1,38 @@
|
|||
package ca.blarg.tiles3.basicexample;
|
||||
|
||||
import ca.blarg.gdx.tilemap3d.Tile;
|
||||
import ca.blarg.gdx.tilemap3d.prefabs.TilePrefab;
|
||||
|
||||
public class Prefabs {
|
||||
public static final TilePrefab tree;
|
||||
|
||||
static {
|
||||
tree = new TilePrefab(5, 6, 5);
|
||||
|
||||
for (int x = 0; x < 5; ++x)
|
||||
for (int z = 0; z < 5; ++z)
|
||||
tree.get(x, 2, z).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(0, 2, 0).set(0, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(4, 2, 0).set(0, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(0, 2, 4).set(0, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(4, 2, 4).set(0, Tile.FLAG_COLLIDEABLE);
|
||||
|
||||
for (int x = 1; x < 4; ++x)
|
||||
for (int z = 1; z < 4; ++z)
|
||||
tree.get(x, 3, z).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(0, 3, 2).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(4, 3, 2).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(2, 3, 0).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(2, 3, 4).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
|
||||
tree.get(2, 4, 2).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(1, 4, 2).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(3, 4, 2).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(2, 4, 1).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(2, 4, 3).set(4, Tile.FLAG_COLLIDEABLE);
|
||||
|
||||
tree.get(2, 0, 2).set(5, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(2, 1, 2).set(5, Tile.FLAG_COLLIDEABLE);
|
||||
tree.get(2, 2, 2).set(5, Tile.FLAG_COLLIDEABLE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package ca.blarg.tiles3.basicexample.entities;
|
||||
|
||||
import com.badlogic.gdx.utils.Pool;
|
||||
|
||||
public class AnimationSequence implements Pool.Poolable {
|
||||
public int start;
|
||||
public int stop;
|
||||
public float delay;
|
||||
public boolean isMultiDirectional;
|
||||
public boolean hasDiagonalDirections;
|
||||
public int directionFrameOffset;
|
||||
|
||||
public AnimationSequence set(int start, int stop, float delay) {
|
||||
this.start = start;
|
||||
this.stop = stop;
|
||||
this.delay = delay;
|
||||
isMultiDirectional = false;
|
||||
hasDiagonalDirections = false;
|
||||
directionFrameOffset = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationSequence set(int start, int stop, float delay, boolean isMultiDirectional, int directionFrameOffset) {
|
||||
this.start = start;
|
||||
this.stop = stop;
|
||||
this.delay = delay;
|
||||
this.isMultiDirectional = isMultiDirectional;
|
||||
this.hasDiagonalDirections = false;
|
||||
this.directionFrameOffset = directionFrameOffset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationSequence set(int start, int stop, float delay, boolean isMultiDirectional, boolean hasDiagonalDirections, int directionFrameOffset) {
|
||||
this.start = start;
|
||||
this.stop = stop;
|
||||
this.delay = delay;
|
||||
this.isMultiDirectional = isMultiDirectional;
|
||||
this.hasDiagonalDirections = hasDiagonalDirections;
|
||||
this.directionFrameOffset = directionFrameOffset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationSequence set(AnimationSequence other) {
|
||||
start = other.start;
|
||||
stop = other.stop;
|
||||
delay = other.delay;
|
||||
isMultiDirectional = other.isMultiDirectional;
|
||||
hasDiagonalDirections = other.hasDiagonalDirections;
|
||||
directionFrameOffset = other.directionFrameOffset;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
start = 0;
|
||||
stop = 0;
|
||||
delay = 0.0f;
|
||||
isMultiDirectional = false;
|
||||
hasDiagonalDirections = false;
|
||||
directionFrameOffset = 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
package ca.blarg.tiles3.basicexample.entities;
|
||||
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
import com.badlogic.gdx.utils.Pool;
|
||||
import com.badlogic.gdx.utils.Pools;
|
||||
|
||||
public class AnimationSequenceCollection implements Pool.Poolable {
|
||||
final ObjectMap<String, AnimationSequence> sequences = new ObjectMap<String, AnimationSequence>();
|
||||
|
||||
public AnimationSequenceCollection() {
|
||||
}
|
||||
|
||||
public AnimationSequence add(String key) {
|
||||
if (sequences.containsKey(key))
|
||||
throw new UnsupportedOperationException("Sequence with that name present in collection already");
|
||||
AnimationSequence sequence = Pools.obtain(AnimationSequence.class);
|
||||
sequences.put(key, sequence);
|
||||
return sequence;
|
||||
}
|
||||
|
||||
public AnimationSequence get(String key) {
|
||||
return sequences.get(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
for (ObjectMap.Entry<String, AnimationSequence> i : sequences.entries())
|
||||
Pools.free(i.value);
|
||||
sequences.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package ca.blarg.tiles3.basicexample.entities;
|
||||
|
||||
public enum EntityState {
|
||||
None,
|
||||
Idle,
|
||||
Walking,
|
||||
Falling,
|
||||
Jumping,
|
||||
Dead,
|
||||
Punching,
|
||||
Shooting,
|
||||
Open,
|
||||
Inactive
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.blarg.tiles3.basicexample.entities;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.PositionComponent;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public class EntityUtils {
|
||||
public static void getInterpolatedPosition(Entity entity, float alpha, Vector3 outPosition) {
|
||||
PositionComponent position = entity.get(PositionComponent.class);
|
||||
outPosition.set(position.lastPosition);
|
||||
outPosition.lerp(position.position, alpha);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
package ca.blarg.tiles3.basicexample.entities;
|
||||
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import ca.blarg.gdx.math.MathHelpers;
|
||||
|
||||
public enum FacingDirection {
|
||||
North(0),
|
||||
NorthEast(1),
|
||||
East(2),
|
||||
SouthEast(3),
|
||||
South(4),
|
||||
SouthWest(5),
|
||||
West(6),
|
||||
NorthWest(7);
|
||||
|
||||
int value;
|
||||
|
||||
private FacingDirection(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* the enum values and static angle constants are set such that:
|
||||
*
|
||||
* 1. east = +x direction
|
||||
* west = -x direction
|
||||
* north = -z direction
|
||||
* south = +z direction
|
||||
*
|
||||
* 2. enum.value() * MULTIPLIER = the corresponding static angle constant for
|
||||
* the enum's direction
|
||||
* (e.g. FacingDirection.East.value() * MULTIPLIER == FacingDirection.EAST_ANGLE)
|
||||
*/
|
||||
|
||||
public static float MULTIPLIER = 45.0f;
|
||||
|
||||
public static float NORTH_ANGLE = 0.0f;
|
||||
public static float NORTHEAST_ANGLE = 45.0f;
|
||||
public static float EAST_ANGLE = 90.0f;
|
||||
public static float SOUTHEAST_ANGLE = 135.0f;
|
||||
public static float SOUTH_ANGLE = 180.0f;
|
||||
public static float SOUTHWEST_ANGLE = 225.0f;
|
||||
public static float WEST_ANGLE = 270.0f;
|
||||
public static float NORTHWEST_ANGLE = 315.0f;
|
||||
|
||||
public static FacingDirection getFacingDirection(float orientationY, boolean limitToQuadrantDirections) {
|
||||
final float QUADRANT_ANGLE_ADJUSTMENT = 45.0f;
|
||||
final float QUADRANT_DIRECTION_DIVISOR = 90.0f;
|
||||
final float OCTANT_ANGLE_ADJUSTMENT = QUADRANT_ANGLE_ADJUSTMENT / 2.0f;
|
||||
final float OCTANT_DIRECTION_DIVISOR = 45.0f;
|
||||
|
||||
float adjustment = limitToQuadrantDirections ? QUADRANT_ANGLE_ADJUSTMENT : OCTANT_ANGLE_ADJUSTMENT;
|
||||
float divisor = limitToQuadrantDirections ? QUADRANT_DIRECTION_DIVISOR : OCTANT_DIRECTION_DIVISOR;
|
||||
|
||||
// add 45 degrees initially to kind of "rotate" the 360 degree circle
|
||||
// clockwise because 315 -> 360 is also part of the "east" quadrant along
|
||||
// with 0 -> 45.
|
||||
float adjusted = (orientationY) + adjustment;
|
||||
if (adjusted >= 360.0f)
|
||||
adjusted -= 360.0f;
|
||||
|
||||
return FacingDirection.values()[(int)(adjusted / divisor)];
|
||||
}
|
||||
|
||||
public static FacingDirection getFacingDirection(Vector3 orientation, boolean limitToQuadrantDirections) {
|
||||
return getFacingDirection(orientation.y, limitToQuadrantDirections);
|
||||
}
|
||||
|
||||
public static FacingDirection getFacingDirectionAdjustedForCamera(float objectOrientationY, float cameraYaw, boolean limitToQuadrantDirections) {
|
||||
// HACK: this pretty much means I should probably just forget about 'adjusting' the angles by 90.0f and such
|
||||
// and just use the "native" orientations...
|
||||
float hackyAdjustedObjectOrientationY = objectOrientationY + 180.0f;
|
||||
|
||||
float difference = MathHelpers.rolloverClamp(hackyAdjustedObjectOrientationY - cameraYaw, 0.0f, 360.0f);
|
||||
return getFacingDirection(difference, limitToQuadrantDirections);
|
||||
}
|
||||
|
||||
public static float getFacingAngle(FacingDirection direction) {
|
||||
return ((float)direction.ordinal()) * MULTIPLIER;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package ca.blarg.tiles3.basicexample.entities;
|
||||
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public final class PhysicsConstants {
|
||||
public static final float UNITS_PER_METRE = 1.0f;
|
||||
|
||||
public static final float GRAVITY_SPEED = 9.8f * UNITS_PER_METRE;
|
||||
public static final Vector3 GRAVITY = new Vector3(0.0f, -GRAVITY_SPEED, 0.0f);
|
||||
|
||||
public static final float FRICTION_NORMAL = 0.5f;
|
||||
|
||||
public static final float SLOPE_STEEP_Y_ANGLE = 46;
|
||||
public static final float ON_GROUND_ZERO_TOLERANCE = 0.1f;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components;
|
||||
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
|
||||
public class CameraComponent extends Component {
|
||||
public boolean useEntityOrientation;
|
||||
public float yAngle;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
useEntityOrientation = false;
|
||||
yAngle = 0.0f;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components;
|
||||
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
|
||||
public class PlayerComponent extends Component {
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components;
|
||||
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
import ca.blarg.tiles3.basicexample.entities.EntityState;
|
||||
|
||||
public class StateComponent extends Component {
|
||||
public EntityState state;
|
||||
public EntityState actionState;
|
||||
public boolean isInActionState;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
state = EntityState.None;
|
||||
actionState = EntityState.None;
|
||||
isInActionState = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components;
|
||||
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
import ca.blarg.gdx.tilemap3d.TileMap;
|
||||
|
||||
public class WorldComponent extends Component {
|
||||
public TileMap tileMap;
|
||||
public boolean renderGrid;
|
||||
public boolean renderSkybox;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
tileMap = null;
|
||||
renderGrid = false;
|
||||
renderSkybox = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components.physics;
|
||||
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.math.collision.Sphere;
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
|
||||
public class BoundingSphereComponent extends Component {
|
||||
public final Sphere bounds = new Sphere(Vector3.Zero, 0.0f);
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
bounds.center.set(Vector3.Zero);
|
||||
bounds.radius = 0.0f;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components.physics;
|
||||
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
import ca.blarg.gdx.math.MathHelpers;
|
||||
|
||||
public class OrientationXZComponent extends Component {
|
||||
public float angle = 0.0f;
|
||||
|
||||
public void getDirectionVector(Vector3 out) {
|
||||
MathHelpers.getDirectionVector3FromYAxis(angle, out);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
angle = 0.0f;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components.physics;
|
||||
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
import ca.blarg.gdx.math.SweptSphere;
|
||||
import ca.blarg.gdx.tilemap3d.Tile;
|
||||
import ca.blarg.gdx.tilemap3d.TileCoord;
|
||||
import ca.blarg.tiles3.basicexample.entities.forces.Force;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.math.collision.Ray;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
import com.badlogic.gdx.utils.Pools;
|
||||
|
||||
public class PhysicsComponent extends Component {
|
||||
public final Vector3 velocity = new Vector3();
|
||||
public final Vector3 lastTickVelocity = new Vector3();
|
||||
public final Vector3 currentTickVelocity = new Vector3();
|
||||
|
||||
public final Vector3 walkingVelocity = new Vector3();
|
||||
public float walkingAcceleration;
|
||||
public float maxWalkSpeed;
|
||||
public float friction;
|
||||
|
||||
public final Array<Force> forces = new Array<Force>(false, 4, Force.class);
|
||||
public final Vector3 forceVelocity = new Vector3();
|
||||
|
||||
public final SweptSphere sweptSphere = new SweptSphere();
|
||||
public final TileCoord collisionTilePosition = new TileCoord();
|
||||
public final TileCoord standingOnTilePosition = new TileCoord();
|
||||
public Tile standingOnTile;
|
||||
|
||||
public boolean isWalking;
|
||||
public boolean wasWalking;
|
||||
public boolean isInMotion;
|
||||
public boolean wasInMotion;
|
||||
public boolean isFalling;
|
||||
public boolean wasFalling;
|
||||
public boolean isOnGround;
|
||||
public boolean wasOnGround;
|
||||
public boolean isSliding;
|
||||
public boolean wasSliding;
|
||||
|
||||
public float fallDistance;
|
||||
public float lastYPosition;
|
||||
public float currentFallDistance;
|
||||
|
||||
public final Ray slidingPlaneNormal = new Ray(Vector3.Zero, Vector3.Zero);
|
||||
public final Vector3 nearestIntersection = new Vector3();
|
||||
|
||||
public PhysicsComponent setMovementProperties(float walkingAcceleration, float maxWalkSpeed, float friction) {
|
||||
this.walkingAcceleration = walkingAcceleration;
|
||||
this.maxWalkSpeed = maxWalkSpeed;
|
||||
this.friction = friction;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PhysicsComponent setBoundsRadius(float radius) {
|
||||
sweptSphere.setRadius(radius);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
velocity.set(Vector3.Zero);
|
||||
lastTickVelocity.set(Vector3.Zero);
|
||||
currentTickVelocity.set(Vector3.Zero);
|
||||
walkingVelocity.set(Vector3.Zero);
|
||||
walkingAcceleration = 0.0f;
|
||||
maxWalkSpeed = 0.0f;
|
||||
friction = 0.0f;
|
||||
for (int i = 0; i < forces.size; ++i)
|
||||
Pools.free(forces.get(i));
|
||||
forces.clear();
|
||||
forceVelocity.set(Vector3.Zero);
|
||||
sweptSphere.reset();
|
||||
collisionTilePosition.set(0, 0, 0);
|
||||
standingOnTilePosition.set(0, 0, 0);
|
||||
standingOnTile = null;
|
||||
isWalking = false;
|
||||
wasWalking = false;
|
||||
isInMotion = false;
|
||||
wasInMotion = false;
|
||||
isFalling = false;
|
||||
wasFalling = false;
|
||||
isOnGround = false;
|
||||
wasOnGround = false;
|
||||
isSliding = false;
|
||||
wasSliding = false;
|
||||
fallDistance = 0.0f;
|
||||
lastYPosition = 0.0f;
|
||||
currentFallDistance = 0.0f;
|
||||
slidingPlaneNormal.origin.set(Vector3.Zero);
|
||||
slidingPlaneNormal.direction.set(Vector3.Zero);
|
||||
nearestIntersection.set(Vector3.Zero);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components.physics;
|
||||
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
|
||||
public class PositionComponent extends Component {
|
||||
public final Vector3 position = new Vector3();
|
||||
public final Vector3 lastPosition = new Vector3();
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
position.set(Vector3.Zero);
|
||||
lastPosition.set(Vector3.Zero);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components.rendering;
|
||||
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
import ca.blarg.tiles3.basicexample.entities.AnimationSequence;
|
||||
import ca.blarg.tiles3.basicexample.entities.AnimationSequenceCollection;
|
||||
|
||||
public class AnimationComponent extends Component {
|
||||
public final AnimationSequenceCollection sequences = new AnimationSequenceCollection();
|
||||
public int currentFrame;
|
||||
public int nextFrame;
|
||||
public float frameTime;
|
||||
public float interpolation;
|
||||
public boolean isLooping;
|
||||
public boolean currentSequenceUnoverrideable;
|
||||
public boolean hasAnimationFinishEventBeenTriggered;
|
||||
public String currentSequenceName;
|
||||
public final AnimationSequence currentSequence = new AnimationSequence();
|
||||
|
||||
public boolean isAnimating() {
|
||||
return (currentSequence.start != currentSequence.stop);
|
||||
}
|
||||
|
||||
public boolean isAnimationFinished() {
|
||||
// let the last frame of a non-looping animation (even a 1-frame
|
||||
// "animation") stay visible for at least the sequence's delay time as long
|
||||
// as it's non-zero
|
||||
if (!isLooping && currentFrame == currentSequence.stop && currentSequence.delay > 0.0f) {
|
||||
if (frameTime < currentSequence.delay)
|
||||
return false; // still working through the last frame time
|
||||
else
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return (isAnimating() && !isLooping && currentFrame == currentSequence.stop);
|
||||
}
|
||||
|
||||
public AnimationComponent setSequence(String name, boolean loop, boolean cannotBeOverridden) {
|
||||
AnimationSequence sequence = sequences.get(name);
|
||||
if (sequence == null)
|
||||
return this;
|
||||
|
||||
currentSequence.set(sequence);
|
||||
currentSequenceName = name;
|
||||
|
||||
currentFrame = currentSequence.start;
|
||||
nextFrame = currentFrame + 1;
|
||||
if (nextFrame > currentSequence.stop)
|
||||
nextFrame = currentSequence.stop;
|
||||
|
||||
frameTime = 0.0f;
|
||||
interpolation = 0.0f;
|
||||
isLooping = loop;
|
||||
currentSequenceUnoverrideable = cannotBeOverridden;
|
||||
hasAnimationFinishEventBeenTriggered = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnimationComponent stopSequence() {
|
||||
currentSequence.reset();
|
||||
currentFrame = 0;
|
||||
nextFrame = 0;
|
||||
frameTime = 0.0f;
|
||||
interpolation = 0.0f;
|
||||
isLooping = false;
|
||||
currentSequenceUnoverrideable = false;
|
||||
hasAnimationFinishEventBeenTriggered = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
sequences.reset();
|
||||
currentFrame = 0;
|
||||
nextFrame = 0;
|
||||
frameTime = 0.0f;
|
||||
interpolation = 0.0f;
|
||||
isLooping = false;
|
||||
currentSequenceUnoverrideable = false;
|
||||
hasAnimationFinishEventBeenTriggered = false;
|
||||
currentSequenceName = null;
|
||||
currentSequence.reset();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components.rendering;
|
||||
|
||||
import com.badlogic.gdx.utils.ObjectMap;
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
import ca.blarg.tiles3.basicexample.entities.EntityState;
|
||||
|
||||
public class AutoAnimationComponent extends Component {
|
||||
public class AutoAnimationSequenceProperties {
|
||||
public String name;
|
||||
public boolean loop;
|
||||
public boolean cannotBeOverridden;
|
||||
}
|
||||
|
||||
public final ObjectMap<EntityState, AutoAnimationSequenceProperties> mappings = new ObjectMap<EntityState, AutoAnimationSequenceProperties>();
|
||||
|
||||
public AutoAnimationComponent set(EntityState state, String name, boolean loop, boolean cannotBeOverridden) {
|
||||
AutoAnimationSequenceProperties props = new AutoAnimationSequenceProperties();
|
||||
props.name = name;
|
||||
props.loop = loop;
|
||||
props.cannotBeOverridden = cannotBeOverridden;
|
||||
mappings.put(state, props);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
mappings.clear();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.components.rendering;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import ca.blarg.gdx.entities.Component;
|
||||
import ca.blarg.gdx.graphics.atlas.TextureAtlas;
|
||||
|
||||
public class BillboardComponent extends Component {
|
||||
public int tileIndex;
|
||||
public float width;
|
||||
public float height;
|
||||
public boolean isAxisAligned;
|
||||
public Texture texture;
|
||||
public TextureAtlas atlas;
|
||||
|
||||
public BillboardComponent set(float width, float height, Texture texture, boolean isAxisAligned) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.texture = texture;
|
||||
this.isAxisAligned = isAxisAligned;
|
||||
this.atlas = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BillboardComponent set(float width, float height, TextureAtlas atlas, int tileIndex, boolean isAxisAligned) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
this.atlas = atlas;
|
||||
this.tileIndex = tileIndex;
|
||||
this.isAxisAligned = isAxisAligned;
|
||||
this.texture = null;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
tileIndex = 0;
|
||||
width = 0.0f;
|
||||
height = 0.0f;
|
||||
isAxisAligned = false;
|
||||
texture = null;
|
||||
atlas = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
import ca.blarg.tiles3.basicexample.entities.EntityState;
|
||||
|
||||
public class AnimationChangeEvent extends Event {
|
||||
public Entity entity;
|
||||
public String sequenceName;
|
||||
public boolean changeToSequenceForState;
|
||||
public EntityState state;
|
||||
public boolean loop;
|
||||
public boolean overrideExisting;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
sequenceName = null;
|
||||
changeToSequenceForState = false;
|
||||
state = EntityState.None;
|
||||
loop = false;
|
||||
overrideExisting = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class AnimationFinishedEvent extends Event {
|
||||
public Entity entity;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class DespawnedEvent extends Event {
|
||||
public Entity entity;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class EntityExitingTempStateEvent extends Event {
|
||||
public Entity entity;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class EntityInTempStateEvent extends Event {
|
||||
public Entity entity;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
import ca.blarg.tiles3.basicexample.entities.EntityState;
|
||||
|
||||
public class EntityStateChangeEvent extends Event {
|
||||
public Entity entity;
|
||||
public EntityState state;
|
||||
public boolean isActionState;
|
||||
public boolean clearExistingActionStateInfo;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
state = EntityState.None;
|
||||
isActionState = false;
|
||||
clearExistingActionStateInfo = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class JumpEvent extends Event {
|
||||
public Entity entity;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class MoveAndTurnEvent extends Event {
|
||||
public Entity entity;
|
||||
public float angle;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
angle = 0.0f;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class MoveForwardEvent extends Event {
|
||||
public Entity entity;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class MoveInDirectionEvent extends Event {
|
||||
public Entity entity;
|
||||
public final Vector3 direction = new Vector3();
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
direction.set(Vector3.Zero);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class SpawnedEvent extends Event {
|
||||
public Entity entity;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.events;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
|
||||
public class StopMovementEvent extends Event {
|
||||
public Entity entity;
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
entity = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.forces;
|
||||
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.utils.Pool;
|
||||
import ca.blarg.gdx.math.MathHelpers;
|
||||
|
||||
public class Force implements Pool.Poolable {
|
||||
boolean active;
|
||||
final Vector3 direction = new Vector3();
|
||||
float strength;
|
||||
float friction;
|
||||
final Vector3 currentForce = new Vector3();
|
||||
float duration;
|
||||
float minDuration;
|
||||
float zeroTolerance = MathHelpers.EPSILON;
|
||||
|
||||
final Vector3 tempCurrentForce = new Vector3();
|
||||
|
||||
public Force set(Vector3 direction, float strength, float friction) {
|
||||
active = true;
|
||||
this.direction.set(direction);
|
||||
this.strength = strength;
|
||||
this.friction = friction;
|
||||
currentForce.set(Vector3.Zero);
|
||||
duration = 0.0f;
|
||||
minDuration = 0.0f;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Force setMinimumDuration(float minDuration) {
|
||||
this.minDuration = minDuration;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Force setZeroTolerance(float tolerance) {
|
||||
this.zeroTolerance = tolerance;
|
||||
return this;
|
||||
}
|
||||
|
||||
public boolean isActive() {
|
||||
return active;
|
||||
}
|
||||
|
||||
public boolean isZeroStrength() {
|
||||
return MathHelpers.areAlmostEqual(strength, 0.0f, zeroTolerance);
|
||||
}
|
||||
|
||||
public boolean hasMinDurationPassed() {
|
||||
return duration >= minDuration;
|
||||
}
|
||||
|
||||
public Vector3 getCurrentForce() {
|
||||
tempCurrentForce.set(currentForce);
|
||||
return tempCurrentForce;
|
||||
}
|
||||
|
||||
public void kill() {
|
||||
active = false;
|
||||
currentForce.set(Vector3.Zero);
|
||||
}
|
||||
|
||||
public void update(float delta) {
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
if (isZeroStrength()) {
|
||||
kill();
|
||||
return;
|
||||
}
|
||||
|
||||
duration += delta;
|
||||
currentForce.set(direction).scl(strength);
|
||||
strength *= friction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
active = false;
|
||||
direction.set(Vector3.Zero);
|
||||
strength = 0.0f;
|
||||
friction = 0.0f;
|
||||
currentForce.set(Vector3.Zero);
|
||||
duration = 0.0f;
|
||||
minDuration = 0.0f;
|
||||
zeroTolerance = MathHelpers.EPSILON;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.forces;
|
||||
|
||||
import ca.blarg.gdx.GameLooper;
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.math.MathHelpers;
|
||||
import ca.blarg.tiles3.basicexample.entities.PhysicsConstants;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.PhysicsComponent;
|
||||
import com.badlogic.gdx.math.MathUtils;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public class JumpForce extends Force {
|
||||
PhysicsComponent physicsComponent;
|
||||
|
||||
public Force set(Vector3 direction, float strength, float friction, PhysicsComponent physicsComponent) {
|
||||
// we will almost certainly want a jump to last beyond 1 update tick
|
||||
float tickFrequency = Services.get(GameLooper.class).getUpdateFrequency();
|
||||
float minimumDuration = tickFrequency + 0.01f;
|
||||
|
||||
super.set(direction, strength, friction)
|
||||
.setMinimumDuration(minimumDuration)
|
||||
.setZeroTolerance(PhysicsConstants.ON_GROUND_ZERO_TOLERANCE);
|
||||
this.physicsComponent = physicsComponent;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(float delta) {
|
||||
super.update(delta);
|
||||
|
||||
// Force.update() call could potentially kill this force and then return
|
||||
// here, so we should check for that first
|
||||
if (!isActive())
|
||||
return;
|
||||
|
||||
boolean cancelled = false;
|
||||
|
||||
// jumping should be cancelled when the top of the entity hits a surface
|
||||
// that is more or less perpendicular to the jumping direction (positive Y).
|
||||
// it should also be stopped if the entity has landed on some ground early
|
||||
// along in the jump (e.g. they jumped up to a ledge)
|
||||
if (physicsComponent.sweptSphere.foundCollision || physicsComponent.isSliding) {
|
||||
if (physicsComponent.isSliding || physicsComponent.slidingPlaneNormal.direction.y > 0.0f) {
|
||||
// if we're sliding, then check the angle
|
||||
float slideYAngle = (float)Math.acos(physicsComponent.sweptSphere.slidingPlaneNormal.dot(Vector3.Y)) * MathUtils.radiansToDegrees;
|
||||
|
||||
// Y axis angle's from 135 to 225 means we hit something overhead.
|
||||
// 180 degrees = hit something perfectly perpendicular to the Y axis
|
||||
// HACK: also we check for a Y angle of zero as that will handle an
|
||||
// edge case where the entity jumped and immediately overhead there
|
||||
// is an obstacle (in this case, most of the time, the slide Y angle
|
||||
// is actually for the collision below their feet...)
|
||||
if ((slideYAngle > 135.0f && slideYAngle < 225.0f) || MathHelpers.areAlmostEqual(slideYAngle, 0.0f))
|
||||
cancelled = true;
|
||||
} else
|
||||
// not sliding, just a full-on collision with something
|
||||
// (collision but not sliding means it's usually a flat area of the
|
||||
// ground), so this is really IsOnGround() == TRUE (??)
|
||||
cancelled = true;
|
||||
}
|
||||
|
||||
// don't kill it, even if there was a collision found above, if the force
|
||||
// hasn't been running for the minimum duration yet
|
||||
if (cancelled && hasMinDurationPassed())
|
||||
kill();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
physicsComponent = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.presets;
|
||||
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.entities.EntityPreset;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
import ca.blarg.gdx.events.EventListener;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.tiles3.basicexample.entities.events.DespawnedEvent;
|
||||
import ca.blarg.tiles3.basicexample.entities.events.SpawnedEvent;
|
||||
import com.badlogic.gdx.utils.Disposable;
|
||||
|
||||
public abstract class BaseObjectEntityPreset extends EntityPreset implements EventListener, Disposable {
|
||||
public final EventManager eventManager;
|
||||
|
||||
public BaseObjectEntityPreset(EntityManager entityManager) {
|
||||
super(entityManager);
|
||||
eventManager = entityManager.eventManager;
|
||||
|
||||
eventManager.addListener(SpawnedEvent.class, this);
|
||||
eventManager.addListener(DespawnedEvent.class, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
eventManager.removeListener(SpawnedEvent.class, this);
|
||||
eventManager.removeListener(DespawnedEvent.class, this);
|
||||
}
|
||||
|
||||
public void onSpawn(Entity entity) {
|
||||
}
|
||||
|
||||
public void onDespawn(Entity entity) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Event e) {
|
||||
if (e instanceof SpawnedEvent) {
|
||||
SpawnedEvent event = (SpawnedEvent)e;
|
||||
if (event.entity.getPresetUsedToCreate() == this.getClass())
|
||||
onSpawn(event.entity);
|
||||
}
|
||||
|
||||
else if (e instanceof DespawnedEvent) {
|
||||
DespawnedEvent event = (DespawnedEvent)e;
|
||||
if (event.entity.getPresetUsedToCreate() == this.getClass())
|
||||
onDespawn(event.entity);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.presets;
|
||||
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.tiles3.basicexample.ContentCache;
|
||||
import ca.blarg.tiles3.basicexample.entities.EntityState;
|
||||
import ca.blarg.tiles3.basicexample.entities.PhysicsConstants;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.CameraComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.PlayerComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.StateComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.*;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.rendering.*;
|
||||
|
||||
public class PlayerPreset extends BaseObjectEntityPreset {
|
||||
public PlayerPreset(EntityManager entityManager) {
|
||||
super(entityManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Entity create(CreationArgs args) {
|
||||
ContentCache content = Services.get(ContentCache.class);
|
||||
|
||||
Entity entity = entityManager.add();
|
||||
entity.add(BoundingSphereComponent.class).bounds.radius = 0.5f;
|
||||
entity.add(StateComponent.class).state = EntityState.Idle;
|
||||
entity.add(PlayerComponent.class);
|
||||
|
||||
CameraComponent camera = entity.add(CameraComponent.class);
|
||||
camera.useEntityOrientation = false;
|
||||
|
||||
BillboardComponent billboard = entity.add(BillboardComponent.class);
|
||||
billboard.isAxisAligned = true;
|
||||
billboard.atlas = content.player;
|
||||
billboard.width = 1.7f;
|
||||
billboard.height = 1.7f;
|
||||
|
||||
AnimationComponent animations = entity.add(AnimationComponent.class);
|
||||
animations.sequences.add("idle").set(0, 0, 0.0f, true, 0);
|
||||
animations.sequences.add("walk").set(8, 9, 0.15f, true, 0);
|
||||
animations.sequences.add("dead").set(32, 32, 0.0f);
|
||||
animations.sequences.add("jump").set(8, 8, 0.0f, true, 1);
|
||||
animations.sequences.add("fall").set(9, 9, 0.0f, true, 1);
|
||||
animations.setSequence("idle", true, false);
|
||||
|
||||
AutoAnimationComponent autoAnimation = entity.add(AutoAnimationComponent.class);
|
||||
autoAnimation.set(EntityState.Idle, "idle", true, false)
|
||||
.set(EntityState.Walking, "walk", true, false)
|
||||
.set(EntityState.Falling, "fall", true, false)
|
||||
.set(EntityState.Jumping, "jump", true, false)
|
||||
.set(EntityState.Dead, "dead", false, true);
|
||||
|
||||
entity.add(PhysicsComponent.class)
|
||||
.setMovementProperties(1.0f, 8.0f, PhysicsConstants.FRICTION_NORMAL)
|
||||
.setBoundsRadius(0.49f);
|
||||
|
||||
PositionComponent position = entity.add(PositionComponent.class);
|
||||
position.position.set(16.0f, 1.5f, 16.0f);
|
||||
position.lastPosition.set(position.position);
|
||||
|
||||
entity.add(OrientationXZComponent.class);
|
||||
|
||||
return entity;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.subsystems;
|
||||
|
||||
import com.badlogic.gdx.graphics.Camera;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.entities.ComponentSystem;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.gdx.graphics.ViewportContext;
|
||||
import ca.blarg.gdx.math.MathHelpers;
|
||||
import ca.blarg.tiles3.basicexample.entities.FacingDirection;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.OrientationXZComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.rendering.AnimationComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.rendering.AutoAnimationComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.rendering.BillboardComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.events.AnimationChangeEvent;
|
||||
import ca.blarg.tiles3.basicexample.entities.events.AnimationFinishedEvent;
|
||||
|
||||
public class AnimationSystem extends ComponentSystem {
|
||||
final ViewportContext viewportContext;
|
||||
|
||||
public AnimationSystem(EntityManager entityManager, EventManager eventManager) {
|
||||
super(entityManager, eventManager);
|
||||
viewportContext = Services.get(ViewportContext.class);
|
||||
|
||||
listenFor(AnimationChangeEvent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopListeningFor(AnimationChangeEvent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateFrame(float delta) {
|
||||
updateAnimationSequences(delta);
|
||||
updateEntityRenderFrames();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Event e) {
|
||||
if (e instanceof AnimationChangeEvent)
|
||||
return handle((AnimationChangeEvent)e);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void updateAnimationSequences(float delta) {
|
||||
for (Entity entity : entityManager.getAllWith(AnimationComponent.class)) {
|
||||
AnimationComponent animation = entity.get(AnimationComponent.class);
|
||||
|
||||
// only update active animation sequences
|
||||
if (!animation.isAnimationFinished()) {
|
||||
animation.frameTime += delta;
|
||||
animation.interpolation += delta / animation.currentSequence.delay;
|
||||
|
||||
if (animation.frameTime >= animation.currentSequence.delay) {
|
||||
// move to the next frame
|
||||
animation.frameTime = 0.0f;
|
||||
animation.interpolation = 0.0f;
|
||||
|
||||
++animation.currentFrame;
|
||||
if (animation.currentFrame > animation.currentSequence.stop) {
|
||||
animation.currentFrame = animation.currentSequence.start;
|
||||
if (!animation.isLooping) {
|
||||
animation.currentSequence.start = animation.currentSequence.stop;
|
||||
animation.currentFrame = animation.currentSequence.stop;
|
||||
animation.nextFrame = animation.currentFrame;
|
||||
animation.frameTime = animation.currentSequence.delay;
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
++animation.nextFrame;
|
||||
if (animation.nextFrame > animation.currentSequence.stop) {
|
||||
if (!animation.isLooping)
|
||||
animation.nextFrame = animation.currentSequence.stop;
|
||||
else
|
||||
animation.nextFrame = animation.currentSequence.start;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// animation has finished (it is not looping and has reached the end)
|
||||
if (!animation.hasAnimationFinishEventBeenTriggered) {
|
||||
// and we haven't yet raised an event to signal this
|
||||
AnimationFinishedEvent finishedEvent = eventManager.create(AnimationFinishedEvent.class);
|
||||
finishedEvent.entity = entity;
|
||||
eventManager.trigger(finishedEvent);
|
||||
|
||||
animation.hasAnimationFinishEventBeenTriggered = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateEntityRenderFrames() {
|
||||
// update texture atlas frame indexes for tile-animation components with
|
||||
// multi-directional animations based on the current camera orientation
|
||||
for (Entity entity : entityManager.getAllWith(AnimationComponent.class)) {
|
||||
AnimationComponent animation = entity.get(AnimationComponent.class);
|
||||
BillboardComponent billboard = entity.get(BillboardComponent.class);
|
||||
if (billboard != null) {
|
||||
OrientationXZComponent orientation = entity.get(OrientationXZComponent.class);
|
||||
updateBillboardFrame(billboard, animation, orientation, viewportContext.getPerspectiveCamera());
|
||||
} else
|
||||
throw new UnsupportedOperationException("Unsupported animation method.");
|
||||
}
|
||||
}
|
||||
|
||||
private boolean handle(AnimationChangeEvent e) {
|
||||
if (e.changeToSequenceForState) {
|
||||
// event is for an animation change to a sequence based on an entity
|
||||
// state provided in the event info. requires an AutoAnimationComponent
|
||||
AutoAnimationComponent autoAnimation = e.entity.get(AutoAnimationComponent.class);
|
||||
if (autoAnimation != null) {
|
||||
// if the entity's auto animation info has a sequence defined for
|
||||
// the state specified in the event, then switch to that sequence
|
||||
AutoAnimationComponent.AutoAnimationSequenceProperties sequenceProps = autoAnimation.mappings.get(e.state);
|
||||
if (sequenceProps != null) {
|
||||
AnimationComponent animation = e.entity.get(AnimationComponent.class);
|
||||
|
||||
if (!animation.currentSequenceUnoverrideable ||
|
||||
animation.isAnimationFinished() ||
|
||||
e.overrideExisting)
|
||||
animation.setSequence(
|
||||
sequenceProps.name,
|
||||
sequenceProps.loop,
|
||||
sequenceProps.cannotBeOverridden
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// we're just changing to a named animation sequence directly
|
||||
AnimationComponent animation = e.entity.get(AnimationComponent.class);
|
||||
animation.setSequence(e.sequenceName, true, false);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static final Vector3 cameraPos = new Vector3();
|
||||
static final Vector3 cameraTarget = new Vector3();
|
||||
private void updateBillboardFrame(BillboardComponent billboard, AnimationComponent animation, OrientationXZComponent orientation, Camera camera) {
|
||||
if (billboard.atlas == null)
|
||||
return;
|
||||
|
||||
if (orientation != null && animation.currentSequence.isMultiDirectional) {
|
||||
// get the direction to point the entity in based on it's own facing
|
||||
// direction and the direction the camera is pointed in
|
||||
cameraPos.set(camera.position);
|
||||
cameraTarget.set(camera.position)
|
||||
.add(camera.direction);
|
||||
float yaw = MathHelpers.getYAngleBetween(cameraTarget, cameraPos);
|
||||
int direction = FacingDirection.getFacingDirectionAdjustedForCamera(orientation.angle, yaw, !animation.currentSequence.hasDiagonalDirections).value();
|
||||
|
||||
// +1 because sequences will be specified using start/stop frames that
|
||||
// are a part of the sequence. e.g. for a 2 frame sequence using frames
|
||||
// 10 and 11, you would specify start=10 and stop=11, so without +1, the
|
||||
// below calc would find the length of this sequence to be 1 which is
|
||||
// not correct...
|
||||
int sequenceLength = animation.currentSequence.stop - animation.currentSequence.start + 1;
|
||||
|
||||
// offset between frames for different directions
|
||||
int offset = direction * animation.currentSequence.directionFrameOffset;
|
||||
|
||||
// set the frame index
|
||||
billboard.tileIndex = animation.currentFrame + (sequenceLength * direction) + offset;
|
||||
} else {
|
||||
billboard.tileIndex = animation.currentFrame;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.subsystems;
|
||||
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.entities.ComponentSystem;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.gdx.graphics.BillboardSpriteBatch;
|
||||
import ca.blarg.gdx.graphics.ViewportContext;
|
||||
import ca.blarg.tiles3.basicexample.entities.EntityUtils;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.BoundingSphereComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.PositionComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.rendering.BillboardComponent;
|
||||
import com.badlogic.gdx.graphics.Camera;
|
||||
import com.badlogic.gdx.math.Frustum;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public class BillboardRenderSystem extends ComponentSystem {
|
||||
private static final float Y_COORD_OFFSET = -1.0f;
|
||||
private final Vector3 renderPosition = new Vector3();
|
||||
|
||||
final ViewportContext viewportContext;
|
||||
final BillboardSpriteBatch billboardSpriteBatch;
|
||||
|
||||
public BillboardRenderSystem(EntityManager entityManager, EventManager eventManager) {
|
||||
super(entityManager, eventManager);
|
||||
viewportContext = Services.get(ViewportContext.class);
|
||||
billboardSpriteBatch = Services.get(BillboardSpriteBatch.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRender(float interpolation) {
|
||||
Camera camera = viewportContext.getPerspectiveCamera();
|
||||
Frustum frustum = camera.frustum;
|
||||
|
||||
billboardSpriteBatch.begin(camera);
|
||||
|
||||
for (Entity entity : entityManager.getAllWith(BillboardComponent.class)) {
|
||||
BillboardComponent billboard = entity.get(BillboardComponent.class);
|
||||
PositionComponent position = entity.get(PositionComponent.class);
|
||||
BoundingSphereComponent bounds = entity.get(BoundingSphereComponent.class);
|
||||
|
||||
if (bounds != null) {
|
||||
if (!frustum.sphereInFrustum(bounds.bounds.center, bounds.bounds.radius))
|
||||
continue;
|
||||
} else {
|
||||
if (!frustum.pointInFrustum(position.lastPosition))
|
||||
continue;
|
||||
}
|
||||
|
||||
EntityUtils.getInterpolatedPosition(entity, interpolation, renderPosition);
|
||||
|
||||
BillboardSpriteBatch.Type billboardType =
|
||||
(billboard.isAxisAligned ? BillboardSpriteBatch.Type.ScreenAligned :
|
||||
BillboardSpriteBatch.Type.Spherical);
|
||||
|
||||
float difference = (billboard.height / 2.0f) + Y_COORD_OFFSET;
|
||||
if (difference > 0.0f)
|
||||
renderPosition.y += difference;
|
||||
renderPosition.x += 0.35f;
|
||||
|
||||
if (billboard.texture != null)
|
||||
billboardSpriteBatch.draw(
|
||||
billboardType, billboard.texture,
|
||||
renderPosition.x, renderPosition.y, renderPosition.z,
|
||||
billboard.width, billboard.height
|
||||
);
|
||||
else
|
||||
billboardSpriteBatch.draw(
|
||||
billboardType, billboard.atlas.get(billboard.tileIndex),
|
||||
renderPosition.x, renderPosition.y, renderPosition.z,
|
||||
billboard.width, billboard.height
|
||||
);
|
||||
}
|
||||
|
||||
billboardSpriteBatch.end();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.subsystems;
|
||||
|
||||
import ca.blarg.gdx.entities.ComponentSystem;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.BoundingSphereComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.PositionComponent;
|
||||
|
||||
public class BoundingVolumeWorldPositioningSystem extends ComponentSystem {
|
||||
public BoundingVolumeWorldPositioningSystem(EntityManager entityManager, EventManager eventManager) {
|
||||
super(entityManager, eventManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateGameState(float delta) {
|
||||
for (Entity entity : entityManager.getAllWith(BoundingSphereComponent.class)) {
|
||||
BoundingSphereComponent bounds = entity.get(BoundingSphereComponent.class);
|
||||
PositionComponent position = entity.get(PositionComponent.class);
|
||||
|
||||
bounds.bounds.center.set(position.position);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.subsystems;
|
||||
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.entities.ComponentSystem;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.gdx.graphics.EulerPerspectiveCamera;
|
||||
import ca.blarg.gdx.graphics.ViewportContext;
|
||||
import ca.blarg.gdx.math.MathHelpers;
|
||||
import ca.blarg.tiles3.basicexample.entities.EntityUtils;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.CameraComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.OrientationXZComponent;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
|
||||
public class CameraSystem extends ComponentSystem {
|
||||
static final float FOLLOW_DISTANCE = 3.5f;
|
||||
static final float FOLLOW_HEIGHT = 9.0f;
|
||||
static final float FOLLOW_PITCH_ANGLE = 70.0f;
|
||||
|
||||
final static Vector3 targetEntityDirection = new Vector3();
|
||||
final static Vector3 targetEntityPosition = new Vector3();
|
||||
final static Vector3 tmp = new Vector3();
|
||||
|
||||
final ViewportContext viewportContext;
|
||||
|
||||
public CameraSystem(EntityManager entityManager, EventManager eventManager) {
|
||||
super(entityManager, eventManager);
|
||||
viewportContext = Services.get(ViewportContext.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRender(float interpolation) {
|
||||
Entity target = entityManager.getFirstWith(CameraComponent.class);
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
CameraComponent cameraProperties = target.get(CameraComponent.class);
|
||||
|
||||
float yAngle;
|
||||
if (cameraProperties.useEntityOrientation)
|
||||
yAngle = target.get(OrientationXZComponent.class).angle;
|
||||
else
|
||||
yAngle = cameraProperties.yAngle;
|
||||
|
||||
EulerPerspectiveCamera camera = (EulerPerspectiveCamera)viewportContext.getPerspectiveCamera();
|
||||
|
||||
MathHelpers.getDirectionVector3FromYAxis(yAngle, targetEntityDirection);
|
||||
EntityUtils.getInterpolatedPosition(target, interpolation, targetEntityPosition);
|
||||
|
||||
// get the new camera position
|
||||
tmp.set(targetEntityDirection) // behind the direction the player is currently facing
|
||||
.scl(-1.0f);
|
||||
MathHelpers.setLengthOf(tmp, FOLLOW_DISTANCE); // FOLLOW_DISTANCE units behind ...
|
||||
tmp.add(0.0f, FOLLOW_HEIGHT, 0.0f) // ... and FOLLOW_HEIGHT units up ...
|
||||
.add(targetEntityPosition); // ... from the player's position
|
||||
|
||||
camera.position.set(tmp);
|
||||
float angle = MathHelpers.getYAngleBetween(targetEntityPosition, tmp) - 90.0f;
|
||||
camera.turnTo(angle);
|
||||
camera.pitchTo(FOLLOW_PITCH_ANGLE);
|
||||
camera.update();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,162 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.subsystems;
|
||||
|
||||
import ca.blarg.gdx.entities.ComponentSystem;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.entities.systemcomponents.InactiveComponent;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.tiles3.basicexample.entities.events.*;
|
||||
import ca.blarg.tiles3.basicexample.entities.EntityState;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.StateComponent;
|
||||
|
||||
public class EntityStateSystem extends ComponentSystem {
|
||||
public EntityStateSystem(EntityManager entityManager, EventManager eventManager) {
|
||||
super(entityManager, eventManager);
|
||||
listenFor(EntityStateChangeEvent.class);
|
||||
listenFor(EntityExitingTempStateEvent.class);
|
||||
listenFor(AnimationFinishedEvent.class);
|
||||
listenFor(DespawnedEvent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopListeningFor(EntityStateChangeEvent.class);
|
||||
stopListeningFor(EntityExitingTempStateEvent.class);
|
||||
stopListeningFor(AnimationFinishedEvent.class);
|
||||
stopListeningFor(DespawnedEvent.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Event e) {
|
||||
if (e instanceof EntityStateChangeEvent)
|
||||
return handle((EntityStateChangeEvent)e);
|
||||
|
||||
else if (e instanceof EntityExitingTempStateEvent)
|
||||
return handle((EntityExitingTempStateEvent)e);
|
||||
|
||||
else if (e instanceof AnimationFinishedEvent)
|
||||
return handle((AnimationFinishedEvent)e);
|
||||
|
||||
else if (e instanceof DespawnedEvent)
|
||||
return handle((DespawnedEvent)e);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handle(EntityStateChangeEvent e)
|
||||
{
|
||||
StateComponent state = e.entity.get(StateComponent.class);
|
||||
if (state != null) {
|
||||
if (e.isActionState) {
|
||||
// set action state first
|
||||
state.actionState = e.state;
|
||||
state.isInActionState = true;
|
||||
|
||||
// HACK: this was mainly added to prevent the death animation from
|
||||
// playing twice in a row in a somewhat rare scenario. however
|
||||
// this does seem to be a sensible check regardless ...
|
||||
boolean needToChangeAnimation = state.state != state.actionState;
|
||||
|
||||
// trigger state change event
|
||||
EntityInTempStateEvent actionStateEvent = eventManager.create(EntityInTempStateEvent.class);
|
||||
actionStateEvent.entity = e.entity;
|
||||
eventManager.trigger(actionStateEvent);
|
||||
|
||||
if (needToChangeAnimation) {
|
||||
// trigger animation change
|
||||
AnimationChangeEvent animationChangeEvent = eventManager.create(AnimationChangeEvent.class);
|
||||
animationChangeEvent.entity = e.entity;
|
||||
animationChangeEvent.changeToSequenceForState = true;
|
||||
animationChangeEvent.state = e.state;
|
||||
eventManager.trigger(animationChangeEvent);
|
||||
}
|
||||
} else {
|
||||
// HACK: this will (hopefully?) stop entity's from doing there
|
||||
// death animation and then popping up to idle before
|
||||
// despawning. need to verify (very hard to reproduce!)
|
||||
if (state.state == EntityState.Dead && e.state == EntityState.Idle)
|
||||
return false;
|
||||
|
||||
// HACK: this was mainly added to prevent the death animation from
|
||||
// playing twice in a row in a somewhat rare scenario. however
|
||||
// this does seem to be a sensible check regardless ...
|
||||
boolean needToChangeAnimation = state.state != state.actionState;
|
||||
|
||||
// set non-action state
|
||||
state.state = e.state;
|
||||
|
||||
if (e.clearExistingActionStateInfo) {
|
||||
state.actionState = EntityState.None;
|
||||
state.isInActionState = false;
|
||||
}
|
||||
|
||||
if (needToChangeAnimation) {
|
||||
// trigger animation change
|
||||
AnimationChangeEvent animationChangeEvent = eventManager.create(AnimationChangeEvent.class);
|
||||
animationChangeEvent.entity = e.entity;
|
||||
animationChangeEvent.changeToSequenceForState = true;
|
||||
animationChangeEvent.state = e.state;
|
||||
|
||||
// HACK: this ensures that the death animation will play
|
||||
animationChangeEvent.overrideExisting = e.state == EntityState.Dead;
|
||||
|
||||
eventManager.trigger(animationChangeEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handle(EntityExitingTempStateEvent e) {
|
||||
StateComponent state = e.entity.get(StateComponent.class);
|
||||
if (state != null) {
|
||||
// HACK: this will (hopefully?) stop entity's from doing their
|
||||
// death animation and then popping up to idle before
|
||||
// despawning. need to verify (very hard to reproduce!)
|
||||
if (state.state == EntityState.Dead)
|
||||
return false;
|
||||
|
||||
// HACK: this was mainly added to prevent the death animation from
|
||||
// playing twice in a row in a somewhat rare scenario. however
|
||||
// this does seem to be a sensible check regardless ...
|
||||
boolean needToChangeAnimation = state.state != state.actionState;
|
||||
|
||||
// clear action state info
|
||||
state.isInActionState = false;
|
||||
state.actionState = EntityState.None;
|
||||
|
||||
if (needToChangeAnimation) {
|
||||
// trigger animation change
|
||||
AnimationChangeEvent animationChangeEvent = eventManager.create(AnimationChangeEvent.class);
|
||||
animationChangeEvent.entity = e.entity;
|
||||
animationChangeEvent.changeToSequenceForState = true;
|
||||
animationChangeEvent.state = state.state;
|
||||
eventManager.trigger(animationChangeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handle(AnimationFinishedEvent e) {
|
||||
StateComponent state = e.entity.get(StateComponent.class);
|
||||
|
||||
// TODO: this all assumes that an AnimationFinishedEvent triggered for
|
||||
// an entity with a StateComponent marked as "in action state"
|
||||
// should *always* trigger an EntityExitingTempStateEvent. Maybe
|
||||
// this should be set up some other way with less assumptions?
|
||||
if (state != null && state.isInActionState) {
|
||||
EntityExitingTempStateEvent exitingTempStateEvent = eventManager.create(EntityExitingTempStateEvent.class);
|
||||
exitingTempStateEvent.entity = e.entity;
|
||||
eventManager.trigger(exitingTempStateEvent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handle(DespawnedEvent e) {
|
||||
e.entity.add(InactiveComponent.class);
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,378 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.subsystems;
|
||||
|
||||
import ca.blarg.gdx.entities.ComponentSystem;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.events.Event;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.gdx.math.MathHelpers;
|
||||
import ca.blarg.gdx.math.SweptSphereHandler;
|
||||
import ca.blarg.gdx.tilemap3d.TileMapSweptSphereCollisionChecker;
|
||||
import ca.blarg.tiles3.basicexample.entities.EntityState;
|
||||
import ca.blarg.tiles3.basicexample.entities.PhysicsConstants;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.WorldComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.*;
|
||||
import ca.blarg.tiles3.basicexample.entities.events.*;
|
||||
import ca.blarg.tiles3.basicexample.entities.forces.Force;
|
||||
import ca.blarg.tiles3.basicexample.entities.forces.JumpForce;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import com.badlogic.gdx.math.collision.Ray;
|
||||
import com.badlogic.gdx.utils.Pools;
|
||||
|
||||
public class PhysicsSystem extends ComponentSystem {
|
||||
private class TerrainFrictionValues {
|
||||
public float friction;
|
||||
public float walkAcceleration;
|
||||
public float maxWalkSpeed;
|
||||
}
|
||||
|
||||
final SweptSphereHandler sweptSphereHandler;
|
||||
public final TileMapSweptSphereCollisionChecker sweptSphereWorldCollisionChecker;
|
||||
|
||||
static final Vector3 tmp1 = new Vector3();
|
||||
final TerrainFrictionValues tmpTerrainFrictionValues = new TerrainFrictionValues();
|
||||
|
||||
public PhysicsSystem(EntityManager entityManager, EventManager eventManager) {
|
||||
super(entityManager, eventManager);
|
||||
listenFor(MoveForwardEvent.class);
|
||||
listenFor(MoveInDirectionEvent.class);
|
||||
listenFor(MoveAndTurnEvent.class);
|
||||
listenFor(StopMovementEvent.class);
|
||||
listenFor(JumpEvent.class);
|
||||
|
||||
sweptSphereWorldCollisionChecker = new TileMapSweptSphereCollisionChecker();
|
||||
sweptSphereHandler = new SweptSphereHandler(sweptSphereWorldCollisionChecker, 5);
|
||||
sweptSphereHandler.possibleCollisionAreaMinOffset.set(0.0f, -0.5f, 0.0f);
|
||||
sweptSphereHandler.possibleCollisionAreaMaxOffset.set(0.0f, 0.5f, 0.0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
stopListeningFor(MoveForwardEvent.class);
|
||||
stopListeningFor(MoveInDirectionEvent.class);
|
||||
stopListeningFor(MoveAndTurnEvent.class);
|
||||
stopListeningFor(StopMovementEvent.class);
|
||||
stopListeningFor(JumpEvent.class);
|
||||
}
|
||||
|
||||
static final Vector3 resultingVelocity = new Vector3();
|
||||
static final Vector3 gravityVelocity = new Vector3();
|
||||
@Override
|
||||
public void onUpdateGameState(float delta) {
|
||||
Entity worldEntity = entityManager.getFirstWith(WorldComponent.class);
|
||||
if (worldEntity == null)
|
||||
return; // no world to simulate physics for!
|
||||
|
||||
WorldComponent world = worldEntity.get(WorldComponent.class);
|
||||
sweptSphereWorldCollisionChecker.tileMap = world.tileMap;
|
||||
|
||||
for (Entity entity : entityManager.getAllWith(PhysicsComponent.class)) {
|
||||
PhysicsComponent physics = entity.get(PhysicsComponent.class);
|
||||
PositionComponent position = entity.get(PositionComponent.class);
|
||||
|
||||
// to accurately check if the entity is walking we need to check this first!
|
||||
// walking only adds velocity in the XZ plane. forces can add velocity in any
|
||||
// direction, so if we added them first, we couldn't just check XZ
|
||||
updateIsWalkingState(physics);
|
||||
|
||||
// apply forces. this directly affects velocity, so it needs to go next!
|
||||
processForces(physics, delta);
|
||||
|
||||
getTerrainFrictionFor(physics, tmpTerrainFrictionValues);
|
||||
|
||||
// apply friction and then combine walking and force velocity's into one
|
||||
// to get the entity's current velocity for this tick
|
||||
physics.walkingVelocity.scl(tmpTerrainFrictionValues.friction);
|
||||
|
||||
if (physics.walkingVelocity.len() <= 0.12f)
|
||||
physics.walkingVelocity.set(Vector3.Zero);
|
||||
if (physics.forceVelocity.len() <= 0.1f)
|
||||
physics.forceVelocity.set(Vector3.Zero);
|
||||
|
||||
physics.velocity.set(physics.walkingVelocity)
|
||||
.add(physics.forceVelocity);
|
||||
|
||||
// update last/current tick velocities
|
||||
physics.lastTickVelocity.set(physics.currentTickVelocity);
|
||||
physics.currentTickVelocity.set(physics.velocity)
|
||||
.scl(delta);
|
||||
|
||||
Vector3 gravity = gravityVelocity.set(PhysicsConstants.GRAVITY)
|
||||
.scl(delta);
|
||||
|
||||
physics.sweptSphere.position.set(position.position);
|
||||
|
||||
resultingVelocity.set(Vector3.Zero);
|
||||
sweptSphereHandler.handleMovement(physics.sweptSphere,
|
||||
physics.currentTickVelocity,
|
||||
gravity,
|
||||
resultingVelocity,
|
||||
true,
|
||||
true,
|
||||
PhysicsConstants.SLOPE_STEEP_Y_ANGLE);
|
||||
|
||||
position.position.set(physics.sweptSphere.position);
|
||||
physics.currentTickVelocity.set(resultingVelocity);
|
||||
physics.collisionTilePosition.set(sweptSphereWorldCollisionChecker.lastCollisionTilePosition);
|
||||
|
||||
physics.isInMotion = physics.sweptSphere.isInMotion;
|
||||
physics.wasInMotion = physics.sweptSphere.wasInMotion;
|
||||
physics.isFalling = physics.sweptSphere.isFalling;
|
||||
physics.wasFalling = physics.sweptSphere.wasFalling;
|
||||
physics.isSliding = physics.sweptSphere.isSliding;
|
||||
physics.wasSliding = physics.sweptSphere.wasSliding;
|
||||
physics.isOnGround = physics.sweptSphere.isOnGround;
|
||||
physics.wasOnGround = physics.sweptSphere.wasOnGround;
|
||||
|
||||
updateStandingTileCoords(physics, position, world);
|
||||
|
||||
triggerStateChangeEvents(physics, entity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handle(Event e) {
|
||||
if (e instanceof MoveInDirectionEvent)
|
||||
return handle((MoveInDirectionEvent)e);
|
||||
|
||||
else if (e instanceof MoveForwardEvent)
|
||||
return handle((MoveForwardEvent)e);
|
||||
|
||||
else if (e instanceof MoveAndTurnEvent)
|
||||
return handle((MoveAndTurnEvent)e);
|
||||
|
||||
else if (e instanceof StopMovementEvent)
|
||||
return handle((StopMovementEvent)e);
|
||||
|
||||
else if (e instanceof JumpEvent)
|
||||
return handle((JumpEvent)e);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handle(MoveInDirectionEvent e) {
|
||||
if (e.entity.has(PhysicsComponent.class)) {
|
||||
move(e.entity, e.direction);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handle(MoveForwardEvent e) {
|
||||
if (e.entity.has(PhysicsComponent.class)) {
|
||||
OrientationXZComponent orientation = e.entity.get(OrientationXZComponent.class);
|
||||
tmp1.set(Vector3.Zero);
|
||||
MathHelpers.getDirectionVector3FromYAxis(orientation.angle, tmp1);
|
||||
move(e.entity, tmp1);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handle(MoveAndTurnEvent e) {
|
||||
if (e.entity.has(PhysicsComponent.class)) {
|
||||
OrientationXZComponent orientation = e.entity.get(OrientationXZComponent.class);
|
||||
orientation.angle = e.angle;
|
||||
tmp1.set(Vector3.Zero);
|
||||
MathHelpers.getDirectionVector3FromYAxis(orientation.angle, tmp1);
|
||||
move(e.entity, tmp1);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handle(StopMovementEvent e) {
|
||||
if (e.entity.has(PhysicsComponent.class)) {
|
||||
stop(e.entity);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean handle(JumpEvent e) {
|
||||
if (e.entity.has(PhysicsComponent.class)) {
|
||||
jump(e.entity);
|
||||
return true;
|
||||
} else
|
||||
return false;
|
||||
}
|
||||
|
||||
private void triggerStateChangeEvents(PhysicsComponent physics, Entity entity) {
|
||||
if (physics.isFalling && !physics.wasFalling) {
|
||||
EntityStateChangeEvent event = eventManager.create(EntityStateChangeEvent.class);
|
||||
event.state = EntityState.Falling;
|
||||
event.entity = entity;
|
||||
eventManager.trigger(event);
|
||||
} else if (physics.wasFalling && physics.isWalking) {
|
||||
EntityStateChangeEvent event = eventManager.create(EntityStateChangeEvent.class);
|
||||
event.state = EntityState.Walking;
|
||||
event.entity = entity;
|
||||
eventManager.trigger(event);
|
||||
} else if (physics.wasFalling && !physics.isFalling) {
|
||||
EntityStateChangeEvent event = eventManager.create(EntityStateChangeEvent.class);
|
||||
event.state = EntityState.Idle;
|
||||
event.entity = entity;
|
||||
eventManager.trigger(event);
|
||||
} else if (physics.isWalking && !physics.wasWalking && !physics.isFalling) {
|
||||
EntityStateChangeEvent event = eventManager.create(EntityStateChangeEvent.class);
|
||||
event.state = EntityState.Walking;
|
||||
event.entity = entity;
|
||||
eventManager.trigger(event);
|
||||
} else if (physics.wasWalking && !physics.isWalking) {
|
||||
EntityStateChangeEvent event = eventManager.create(EntityStateChangeEvent.class);
|
||||
event.state = EntityState.Idle;
|
||||
event.entity = entity;
|
||||
eventManager.trigger(event);
|
||||
}
|
||||
}
|
||||
|
||||
static final Vector3 tmpMovementVelocity = new Vector3();
|
||||
private void move(Entity entity, Vector3 direction) {
|
||||
PhysicsComponent physics = entity.get(PhysicsComponent.class);
|
||||
|
||||
getTerrainFrictionFor(physics, tmpTerrainFrictionValues);
|
||||
|
||||
float acceleration = tmpTerrainFrictionValues.walkAcceleration;
|
||||
float maxSpeed = tmpTerrainFrictionValues.maxWalkSpeed;
|
||||
|
||||
// currentVelocity = a * maxVelocity + (1 - a) * currentVelocity
|
||||
|
||||
tmpMovementVelocity.set(direction).scl(maxSpeed) // maxVelocity
|
||||
.scl(acceleration); // * a
|
||||
|
||||
physics.walkingVelocity.scl(1 - acceleration) // (1 - a) * currentVelocity
|
||||
.add(tmpMovementVelocity); // + (a * maxVelocity)
|
||||
}
|
||||
|
||||
private void stop(Entity entity) {
|
||||
PhysicsComponent physics = entity.get(PhysicsComponent.class);
|
||||
physics.velocity.set(Vector3.Zero);
|
||||
physics.walkingVelocity.set(Vector3.Zero);
|
||||
}
|
||||
|
||||
private void jump(Entity entity) {
|
||||
PhysicsComponent physics = entity.get(PhysicsComponent.class);
|
||||
|
||||
if (!physics.isOnGround)
|
||||
return;
|
||||
|
||||
JumpForce newForce = Pools.obtain(JumpForce.class);
|
||||
newForce.set(MathHelpers.UP_VECTOR3, PhysicsConstants.GRAVITY_SPEED * 3.0f, 0.6f, physics);
|
||||
applyForce(physics, newForce);
|
||||
}
|
||||
|
||||
private void applyForce(PhysicsComponent physics, Force force) {
|
||||
physics.forces.add(force);
|
||||
}
|
||||
|
||||
private void applyForce(PhysicsComponent physics, Vector3 direction, float strength, float friction) {
|
||||
Force newForce = Pools.obtain(Force.class);
|
||||
newForce.set(direction, strength, friction);
|
||||
applyForce(physics, newForce);
|
||||
}
|
||||
|
||||
private void processForces(PhysicsComponent physics, float delta) {
|
||||
physics.forceVelocity.set(Vector3.Zero);
|
||||
for (Force force : physics.forces) {
|
||||
force.update(delta);
|
||||
physics.forceVelocity.add(force.getCurrentForce());
|
||||
}
|
||||
|
||||
// clean up dead forces
|
||||
int i = 0;
|
||||
while (i < physics.forces.size) {
|
||||
Force force = physics.forces.get(i);
|
||||
if (!force.isActive()) {
|
||||
// stay at this index for the next iteration since we just removed something (next element now at this same index)
|
||||
physics.forces.removeIndex(i);
|
||||
Pools.free(force);
|
||||
} else
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateIsWalkingState(PhysicsComponent physics) {
|
||||
physics.wasWalking = physics.isWalking;
|
||||
|
||||
if (!physics.isOnGround) {
|
||||
physics.isWalking = false;
|
||||
return;
|
||||
}
|
||||
|
||||
tmp1.set(physics.walkingVelocity);
|
||||
tmp1.y = 0.0f; // walking only allows the entity to get a velocity in the XZ plane
|
||||
|
||||
float velocityLength = tmp1.len();
|
||||
if (MathHelpers.areAlmostEqual(velocityLength, 0.0f))
|
||||
physics.isWalking = false;
|
||||
else
|
||||
physics.isWalking = true;
|
||||
}
|
||||
|
||||
static final Vector3 tmpFeetPosition = new Vector3();
|
||||
static final Ray downRay = new Ray(Vector3.Zero, Vector3.Zero);
|
||||
static final Vector3 tmpCollisionPoint = new Vector3();
|
||||
private void updateStandingTileCoords(PhysicsComponent physics, PositionComponent position, WorldComponent world) {
|
||||
if (physics.isOnGround) {
|
||||
int feetX = (int)position.position.x;
|
||||
int feetY = (int)(position.position.y - physics.sweptSphere.radius.y - 0.01f);
|
||||
int feetZ = (int)position.position.z;
|
||||
|
||||
// prefer to take the tile just below the entity's feet
|
||||
// however, if that tile is empty, take the collision tile
|
||||
// (this can sometimes work better, as the collision tile could
|
||||
// be a tile that the entity is just standing on the very edge of, but
|
||||
// in fact be mostly standing on an adjacent tile... which could be the
|
||||
// one we calculate to be under the entity's feet)
|
||||
|
||||
physics.standingOnTile = world.tileMap.get(feetX, feetY, feetZ);
|
||||
if (!physics.standingOnTile.isEmptySpace()) {
|
||||
// check the distance from the entity's feet to the nearest
|
||||
// collidable surface on this tile before we accept it as the
|
||||
// "standing on" tile
|
||||
tmpFeetPosition.set(position.position);
|
||||
tmpFeetPosition.y -= physics.sweptSphere.radius.y;
|
||||
downRay.set(tmpFeetPosition, MathHelpers.DOWN_VECTOR3);
|
||||
tmpCollisionPoint.set(Vector3.Zero);
|
||||
float collisionDistance = 0.0f;
|
||||
if (world.tileMap.checkForCollisionWithTileMesh(downRay, feetX, feetY, feetZ, world.tileMap.tileMeshes, tmpCollisionPoint))
|
||||
collisionDistance = tmp1.set(tmpFeetPosition)
|
||||
.sub(tmpCollisionPoint)
|
||||
.len();
|
||||
|
||||
// TODO: this distance check causes issues with ramps. as you walk
|
||||
// up/down the ramp, the "standing tile" tends to flip back
|
||||
// and forth a bit depending on how far up or down the ramp
|
||||
// you are from the top, assuming the top of the ramp is
|
||||
// next to a flat surface
|
||||
if (collisionDistance <= 0.1f) {
|
||||
// the top of the foot tile is close enough, use it
|
||||
physics.standingOnTilePosition.set(feetX, feetY, feetZ);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// use the collision tile
|
||||
physics.standingOnTile = world.tileMap.get(physics.collisionTilePosition.x, physics.collisionTilePosition.y, physics.collisionTilePosition.z);
|
||||
physics.standingOnTilePosition.set(physics.collisionTilePosition);
|
||||
} else {
|
||||
// not standing on anything
|
||||
physics.standingOnTilePosition.set(0, 0, 0);
|
||||
physics.standingOnTile = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void getTerrainFrictionFor(PhysicsComponent physics, final TerrainFrictionValues out) {
|
||||
out.friction = physics.friction;
|
||||
out.walkAcceleration = physics.walkingAcceleration;
|
||||
out.maxWalkSpeed = physics.maxWalkSpeed;
|
||||
|
||||
if (physics.isOnGround) {
|
||||
if (physics.standingOnTile.isSlippery()) {
|
||||
out.friction = 0.95f;
|
||||
out.walkAcceleration = 0.5f;
|
||||
out.maxWalkSpeed = 2.0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.subsystems;
|
||||
|
||||
import ca.blarg.gdx.entities.ComponentSystem;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.PositionComponent;
|
||||
|
||||
public class PreviousPositionSystem extends ComponentSystem {
|
||||
public PreviousPositionSystem(EntityManager entityManager, EventManager eventManager) {
|
||||
super(entityManager, eventManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdateGameState(float delta) {
|
||||
for (Entity entity : entityManager.getAllWith(PositionComponent.class)) {
|
||||
PositionComponent position = entity.get(PositionComponent.class);
|
||||
position.lastPosition.set(position.position);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
package ca.blarg.tiles3.basicexample.entities.subsystems;
|
||||
|
||||
import ca.blarg.tiles3.basicexample.entities.components.WorldComponent;
|
||||
import ca.blarg.tiles3.basicexample.entities.components.physics.PositionComponent;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g3d.ModelBatch;
|
||||
import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
|
||||
import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
|
||||
import com.badlogic.gdx.graphics.g3d.Environment;
|
||||
import com.badlogic.gdx.graphics.glutils.ShapeRenderer;
|
||||
import com.badlogic.gdx.math.Vector3;
|
||||
import ca.blarg.gdx.Services;
|
||||
import ca.blarg.gdx.entities.ComponentSystem;
|
||||
import ca.blarg.gdx.entities.Entity;
|
||||
import ca.blarg.gdx.entities.EntityManager;
|
||||
import ca.blarg.gdx.events.EventManager;
|
||||
import ca.blarg.gdx.graphics.GraphicsHelpers;
|
||||
import ca.blarg.gdx.graphics.ViewportContext;
|
||||
import ca.blarg.gdx.math.MathHelpers;
|
||||
import ca.blarg.gdx.tilemap3d.TileMapRenderer;
|
||||
|
||||
public class WorldRendererSystem extends ComponentSystem {
|
||||
final static Color AMBIENT_LIGHT = new Color(0.7f, 0.7f, 0.7f, 1.0f);
|
||||
final static Color DIRECTIONAL_LIGHT = new Color(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
final static Vector3 renderPosition = new Vector3();
|
||||
|
||||
final TileMapRenderer tileMapRenderer;
|
||||
final Environment environment;
|
||||
|
||||
final ViewportContext viewportContext;
|
||||
final ModelBatch modelBatch;
|
||||
final ShapeRenderer shapeRenderer;
|
||||
|
||||
public WorldRendererSystem(EntityManager entityManager, EventManager eventManager) {
|
||||
super(entityManager, eventManager);
|
||||
|
||||
viewportContext = Services.get(ViewportContext.class);
|
||||
modelBatch = Services.get(ModelBatch.class);
|
||||
shapeRenderer = Services.get(ShapeRenderer.class);
|
||||
|
||||
tileMapRenderer = new TileMapRenderer();
|
||||
|
||||
environment = new Environment();
|
||||
environment.set(new ColorAttribute(ColorAttribute.AmbientLight, AMBIENT_LIGHT));
|
||||
environment.add(new DirectionalLight().set(DIRECTIONAL_LIGHT, MathHelpers.LEFT_VECTOR3));
|
||||
environment.add(new DirectionalLight().set(DIRECTIONAL_LIGHT, MathHelpers.RIGHT_VECTOR3));
|
||||
environment.add(new DirectionalLight().set(DIRECTIONAL_LIGHT, MathHelpers.DOWN_VECTOR3));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRender(float interpolation) {
|
||||
Entity worldEntity = entityManager.getFirstWith(WorldComponent.class);
|
||||
if (worldEntity == null)
|
||||
return;
|
||||
|
||||
WorldComponent world = worldEntity.get(WorldComponent.class);
|
||||
PositionComponent worldPosition = worldEntity.get(PositionComponent.class);
|
||||
if (worldPosition != null)
|
||||
renderPosition.set(worldPosition.position);
|
||||
else
|
||||
renderPosition.set(Vector3.Zero);
|
||||
|
||||
if (world.renderSkybox) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
if (world.renderGrid)
|
||||
GraphicsHelpers.renderGridPlane(shapeRenderer, viewportContext.getPerspectiveCamera(),
|
||||
world.tileMap.getWidth(), world.tileMap.getDepth(),
|
||||
renderPosition.x, renderPosition.y, renderPosition.z);
|
||||
|
||||
modelBatch.begin(viewportContext.getPerspectiveCamera());
|
||||
tileMapRenderer.render(modelBatch, world.tileMap, viewportContext.getPerspectiveCamera(), environment);
|
||||
modelBatch.end();
|
||||
}
|
||||
}
|
45
desktop/build.gradle
Normal file
45
desktop/build.gradle
Normal file
|
@ -0,0 +1,45 @@
|
|||
apply plugin: "java"
|
||||
|
||||
sourceCompatibility = 1.7
|
||||
sourceSets.main.java.srcDirs = [ "src/main/java/" ]
|
||||
project.ext.mainClassName = "ca.blarg.tiles3.basicexample.desktop.DesktopStarter"
|
||||
project.ext.assetsDir = new File("../assets");
|
||||
|
||||
task run(dependsOn: classes, type: JavaExec) {
|
||||
main = project.mainClassName
|
||||
classpath = sourceSets.main.runtimeClasspath
|
||||
standardInput = System.in
|
||||
workingDir = project.assetsDir
|
||||
ignoreExitValue = true
|
||||
}
|
||||
|
||||
task dist(type: Jar) {
|
||||
from files(sourceSets.main.output.classesDir)
|
||||
from files(sourceSets.main.output.resourcesDir)
|
||||
from {configurations.compile.collect {zipTree(it)}}
|
||||
from files(project.assetsDir);
|
||||
|
||||
manifest {
|
||||
attributes 'Main-Class': project.mainClassName
|
||||
}
|
||||
}
|
||||
|
||||
dist.dependsOn classes
|
||||
|
||||
eclipse {
|
||||
project {
|
||||
name = appName + "-desktop"
|
||||
linkedResource name: 'assets', type: '2', location: 'PARENT-1-PROJECT_LOC/assets'
|
||||
}
|
||||
}
|
||||
|
||||
task afterEclipseImport(description: "Post processing after project generation", group: "IDE") {
|
||||
doLast {
|
||||
def classpath = new XmlParser().parse(file(".classpath"))
|
||||
new Node(classpath, "classpathentry", [ kind: 'src', path: 'assets' ]);
|
||||
def writer = new FileWriter(file(".classpath"))
|
||||
def printer = new XmlNodePrinter(new PrintWriter(writer))
|
||||
printer.setPreserveWhitespace(true)
|
||||
printer.print(classpath)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package ca.blarg.tiles3.basicexample.desktop;
|
||||
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
|
||||
import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration;
|
||||
import ca.blarg.gdx.GdxGameAppListener;
|
||||
import ca.blarg.tiles3.basicexample.BasicExampleGameApp;
|
||||
|
||||
public class DesktopStarter {
|
||||
public static void main(String[] args) {
|
||||
LwjglApplicationConfiguration cfg = new LwjglApplicationConfiguration();
|
||||
cfg.title = "Tiles³ Basic Example";
|
||||
cfg.width = 1280;
|
||||
cfg.height = 720;
|
||||
new LwjglApplication(new GdxGameAppListener(BasicExampleGameApp.class), cfg);
|
||||
}
|
||||
}
|
BIN
screenshot.png
Normal file
BIN
screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 140 KiB |
1
settings.gradle
Normal file
1
settings.gradle
Normal file
|
@ -0,0 +1 @@
|
|||
include "core", "desktop"
|
Reference in a new issue