LibGDX: Drawing 50x50 tiles is very slow - java

I found some code on the libgdx GitHub that creates a tiled map and populates it. It all works fine until you enlarge the camera to draw ~50x50 tiles. At this point, I get ~5 fps and input is barely responding. Here is the modified code:
public class Core extends ApplicationAdapter {
private AssetManager am;
private SpriteBatch batch;
private BitmapFont font;
private TiledMap map;
private OrthogonalTiledMapRenderer renderer;
private OrthographicCamera camera;
private OrthoCamController cameraController;
#Override
public void create() {
am = new AssetManager();
am.load("tiles.png", Texture.class);
am.finishLoading();
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
batch = new SpriteBatch();
batch.disableBlending();
camera = new OrthographicCamera();
camera.setToOrtho(false, (w / h) * 400, 400);
camera.update();
cameraController = new OrthoCamController(camera);
Gdx.input.setInputProcessor(cameraController);
font = new BitmapFont();
map = new TiledMap();
MapLayers layers = map.getLayers();
TiledMapTileLayer layer = new TiledMapTileLayer(200, 200, 8, 8);
TextureRegion[][] splitTiles = TextureRegion.split((Texture)am.get("tiles.png"), 8, 8);
for (int x = 0; x < 1000; x++) {
for (int y = 0; y < 1000; y++) {
int ty = 1;
int tx = 1;
Cell cell = new Cell();
cell.setTile(new StaticTiledMapTile(splitTiles[ty][tx]));
layer.setCell(x, y, cell);
}
layers.add(layer);
}
renderer = new OrthogonalTiledMapRenderer(map, batch);
}
#Override
public void render() {
Gdx.gl.glClearColor(100f / 255f, 100f / 255f, 250f / 255f, 1f);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
camera.update();
renderer.setView(camera);
renderer.render();
}
#Override
public void resize(int width, int height) {
}
#Override
public void dispose() {
am.dispose();
map.dispose();
}
}
I seriously doubt that Libgdx can't handle that many tiles so my question is: how do I improve performance... or am I being too ambitious?
The only solution I could find was here but it looks like they removed the method used in it.

You probably want to put layers.add(layer); outside the for (int x = 0; x < 1000; x++) loop

Related

Sprite Animation Pictures are Overlapping

I'm trying to make an animation out of an png file, that has various states of an action. My problem is, that the pictures overlap each other while rendering. Is there a solution, where i can only show one picture?
I use the LibGDX lib.
#Override public void show()
{
batch = new SpriteBatch();
img = new Texture("core/assets/ghosty.png");
regions = TextureRegion.split(img, 32, 32);
sprite = new Sprite (regions[0][0]);
Timer.schedule(new Timer.Task(){
#Override
public void run(){
frame++;
if (frame>27){
frame = 0;
if (zeile ==1){
zeile = 0;
}
else
{
zeile = 1;
}
}
sprite.setRegion(regions[zeile][frame]);
}
}, 0, 1/20f);
}
#Override public void render(float delta)
{
//stage.draw();
batch.begin();
sprite.draw(batch);
batch.end();
}
You don't need an extra Timer task and a Sprite instead you need an Animation<>.
Here is a little example of how you render an Animation:
private SpriteBatch batch;
private Texture img;
private Animation<TextureRegion> animation;
private TextureRegion[][] regions;
private Array<TextureRegion> frames;
#Override
public void show() {
batch = new SpriteBatch();
img = new Texture(Gdx.files.internal("ghosty.png")); //Get Texture from asset folder
regions = TextureRegion.split(img, 32, 32);
frames = new Array<TextureRegion>();
int rows = 5, columns = 5; //How many rows and columns the region have
//Fill Frames array with the regions of Texture
for(int i = 0; i < rows; i++){
for(int j = 0; j < columns; j++){
frames.add(regions[i][j]);
}
}
//Create Animation. 0.1f is the time how long a frame will occur,
//is the animation to fast set this number to a higher value
//so the single frames will stay for a longer time
animation = new Animation<TextureRegion>(0.1f, frames, Animation.PlayMode.LOOP);
}
private float stateTime = 0;
#Override
public void render(float delta) {
//Clear the screen
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//update state time so animation will go further
stateTime += delta;
batch.begin();
//Draw the current frame
batch.draw(animation.getKeyFrame(stateTime), 50, 50);
batch.end();
}
Hope this will help you.
A more efficient and easier way to run animation is to use TextureAtlas instead of Texture. Here is an example for using TextureAtlas: Libgdx Animation not working

Java libGDX Game - Animation doesn't flip

I have a animation which i want to flip to the left if key LEFT is pressed, but it doesnt stay flipped. It only flips like 1 frame then turns back again.
Here is my GameScreen where i draw everything:
public class GameScreen extends ScreenManager{
//For the view of the game and the rendering
private SpriteBatch batch;
private OrthographicCamera cam;
//DEBUG
private Box2DDebugRenderer b2dr;
//World, Player and so on
private GameWorld world;
private Player player;
private Ground ground;
//player animations
private TextureRegion currFrame;
public static float w, h;
public GameScreen(Game game) {
super(game);
//vars
w = Gdx.graphics.getWidth();
h = Gdx.graphics.getHeight();
//view and rendering
batch = new SpriteBatch();
cam = new OrthographicCamera();
cam.setToOrtho(false, w/2, h/2);
//debug
b2dr = new Box2DDebugRenderer();
//world, bodies ...
world = new GameWorld();
player = new Player(world);
ground = new Ground(world);
}
#Override
public void pause() {
}
#Override
public void show() {
}
#Override
public void render(float delta) {
//clearing the screen
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//updating
update(Gdx.graphics.getDeltaTime());
player.stateTime += Gdx.graphics.getDeltaTime();
//render
batch.setProjectionMatrix(cam.combined);
currFrame = Player.anim.getKeyFrame(Player.stateTime, true);
batch.begin();
batch.draw(currFrame, Player.body.getPosition().x * PPM - 64, Player.getBody().getPosition().y * PPM- 72);
batch.end();
//debug
b2dr.render(GameWorld.getWorld(), cam.combined.scl(PPM));
}
#Override
public void resize(int width, int height) {
}
#Override
public void hide() {
}
#Override
public void dispose() {
}
#Override
public void onKlick(float delta) {
}
public void update(float delta){
world.update(delta);
updateCam(delta);
Player.keyInput(delta);
System.out.println("X-POS" + Player.getBody().getPosition().x);
System.out.println("Y-POS" + Player.getBody().getPosition().y);
}
public void updateCam(float delta){
Vector3 pos = cam.position;
pos.x = Player.getBody().getPosition().x * PPM;
pos.y = Player.getBody().getPosition().y * PPM;
cam.position.set(pos);
cam.update();
}
}
and this is the Player class where the animation is:
public class Player {
public static Body body;
public static BodyDef def;
private FixtureDef fd;
//textures
public static Texture texture;
public static Sprite sprite;
public static TextureRegion[][] region;
public static TextureRegion[] idle;
public static Animation<TextureRegion> anim;
public static float stateTime;
//set form
private PolygonShape shape;
private GameScreen gs;
public Player(GameWorld world){
texture = new Texture(Gdx.files.internal("player/char_animation_standing.png"));
region = TextureRegion.split(texture, texture.getWidth() / 3, texture.getHeight() / 2);
idle = new TextureRegion[6];
int index = 0;
for(int i = 0; i < 2; i++){
for(int j = 0; j < 3; j++){
sprite = new Sprite(region[i][j]);
idle[index++] = sprite;
}
}
anim = new Animation<TextureRegion>(1 / 8f, idle);
stateTime = 0f;
def = new BodyDef();
def.fixedRotation = true;
def.position.set(gs.w / 4, gs.h / 4);
def.type = BodyType.DynamicBody;
body = world.getWorld().createBody(def);
shape = new PolygonShape();
shape.setAsBox(32 / 2 / PPM, 64/ 2 / PPM);
fd = new FixtureDef();
fd.shape = shape;
fd.density = 30;
body.createFixture(fd);
shape.dispose();
}
public static Body getBody() {
return body;
}
public static BodyDef getDef() {
return def;
}
public static Texture getTexture() {
return texture;
}
public static void keyInput(float delta){
int horizonForce = 0;
if(Gdx.input.isKeyJustPressed(Input.Keys.UP)){
body.applyLinearImpulse(0, 300f, body.getWorldCenter().x, body.getWorldCenter().y, true);
//body.applyForceToCenter(0, 1200f, true);
System.out.println("PRESSED");
}
if(Gdx.input.isKeyPressed(Input.Keys.LEFT)){
horizonForce -= 1;
sprite.flip(!sprite.isFlipX(), sprite.isFlipY());
}
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)){
horizonForce += 1;
}
body.setLinearVelocity(horizonForce * 20, body.getLinearVelocity().y);
}
}
thank you in advance and any answer is appreciated :D
Your sprite variable contain only one frame at the time of pressing left key. So, it flip that current sprite of your animation frame.
To solve the Problem you have to flip all the animation frame on pressing the left key.
You're only flipping last frame of Animation by sprite reference, You need to flip all frames of your Animation anim. You can flip in this way :
if(keycode== Input.Keys.RIGHT) {
for (TextureRegion textureRegion:anim.getKeyFrames())
if(!textureRegion.isFlipX()) textureRegion.flip(true,false);
}
else if(keycode==Input.Keys.LEFT) {
for (TextureRegion textureRegion:anim.getKeyFrames())
if(textureRegion.isFlipX()) textureRegion.flip(true,false);
}

Code for Sprite in Different Class

I want to make a game wherein when the main character sprite's X coordinate is less than the middle of the screen, he moves to the right and when it's more than the middle of the screen, he moves to the left. The sprite's animation changes when he is moving and when he is still (after reaching its destination). What I want to know is how can I do this when the sprite and its code for animation is in one class and the code for changing its X coordinate is in another class? Is it best to draw the same sprite in every class? How can I change the sprite when it is moving horizontally and when it is still? I plan to separate the code for what the sprite's actions will be in different classes because i want to call them randomly. Here is my code for the sprite's animation and i have no class for changing the x coordinate yet :
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Animation;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.jpfalmazan.ninjaassault.NinjaAssault;
public class MainScreen implements Screen {
private static final int FRAME_COLS = 3;
private static final int FRAME_ROWS = 2;
Animation walkAnimation;
Texture walkSheet = new Texture ("ninjaWalk.png");
TextureRegion[] walkFrames;
TextureRegion currentFrame;
Texture holdStart;
float stateTime;
public Texture background;
private NinjaAssault game;
private Music BackgroundSFX;
public float screenWidth = Gdx.graphics.getWidth();
public float screenHeight = Gdx.graphics.getHeight();
float x = screenWidth/2;
float y = screenHeight/2;
public float walkSheetWidth = walkSheet.getWidth();
public float walkSheetHeight = walkSheet.getHeight();
public MainScreen (NinjaAssault game){
this.game = game;
}
#Override
public void show(){
background = new Texture("BGBlue.png");
holdStart = new Texture ("HoldStart.png");
BackgroundSFX = Gdx.audio.newMusic(Gdx.files.internal("data/RADWIMPS-iindesuka.mp3"));
TextureRegion[][] tmp = TextureRegion.split(walkSheet, (int )walkSheetWidth/FRAME_COLS, (int) walkSheetHeight/FRAME_ROWS);
walkFrames = new TextureRegion[FRAME_COLS*FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++){
for (int j = 0; j < FRAME_COLS; j++) {
walkFrames[index++] = tmp[i][j];
}
}
walkAnimation = new Animation(0.0887f, walkFrames);
stateTime = 0f;
}
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
stateTime += Gdx.graphics.getDeltaTime();
currentFrame = walkAnimation.getKeyFrame(stateTime, true);
float hsWidth = holdStart.getWidth();
float hsHeight = holdStart.getHeight();
float currentFrameWidth = (float)(screenHeight*0.15);
float currentFrameHeight = (float)(screenHeight*0.15);
float holdStartWidth = (float)(screenWidth * 0.75);
game.batch.begin();
game.batch.draw(background,0,0, screenWidth,screenHeight);
BackgroundSFX.play();
game.batch.draw(currentFrame, x -currentFrameWidth/2, 0,currentFrameWidth,currentFrameHeight);
game.batch.draw(holdStart, (screenWidth / 2 - (holdStartWidth / 2)), (float) (screenHeight * 0.5), holdStartWidth, holdStartWidth * (hsHeight / hsWidth));
game.batch.end();
}
#Override
public void resize(int width, int height) {
}
#Override
public void pause() {
}
#Override
public void resume() {
}
#Override
public void hide() {
}
#Override
public void dispose() {
BackgroundSFX.dispose();
background.dispose();
walkSheet.dispose();
holdStart.dispose();
}
}
I tried to research on how to do this but the answers I get wasn't that helpful.
Simplest way is to create a Player class and put all the code for the animation and frames in there. Then give it a way to save its position. Like a vector or just a float for the x coordinate. Or even better, use a Rectangle. A rectangle will make moving and collision detection much easier.
Something like this:
public class Player{
private Animation walkAnimation;
private Texture walkSheet;
private TextureRegion[] walkFrames;
private TextureRegion currentFrame;
private float stateTime;
private Rectangle bound; //used for positioning and collision detection
public Player(float x, float y, float width, float height){
bound = new Rectangle();
bound.x = x;
bound.y = y;
bound.width = width;
bound.height = height;
walkSheet = new Texture ("ninjaWalk.png");
TextureRegion[][] tmp = TextureRegion.split(walkSheet,(int)walkSheetWidth/FRAME_COLS, (int) walkSheetHeight/FRAME_ROWS);
walkFrames = new TextureRegion[FRAME_COLS*FRAME_ROWS];
int index = 0;
for (int i = 0; i < FRAME_ROWS; i++){
for (int j = 0; j < FRAME_COLS; j++) {
walkFrames[index++] = tmp[i][j];
}
}
walkAnimation = new Animation(0.0887f, walkFrames);
stateTime = 0f;
}
public rectangle getBound(){
return bound;
}
public void update(float delta){
statetime += delta;
currentFrame = walkAnimation.getKeyFrame(stateTime, true);
}
public TextureRegion getCurrentFrame(){
return currentFrame;
}
}
This is just a quick untested example.
You say you want to move the player from another class. I don't know how you plan to do that, but all you need to do to move the player is to manipulate the x and y of the bound.
Just some other comments on you code:
#Override
public void render(float delta) {
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
player.update(delta); // to update the player
/***
* This does not have to be set every single frame. Move it to show()
*
float hsWidth = holdStart.getWidth();
float hsHeight = holdStart.getHeight();
float currentFrameWidth = (float)(screenHeight*0.15);
float currentFrameHeight = (float)(screenHeight*0.15);
float holdStartWidth = (float)(screenWidth * 0.75);
****************************************************/
BackgroundSFX.play(); // I am sure you don't need to start playing this every single frame? 60 times a second.
game.batch.begin();
game.batch.draw(background,0,0, screenWidth,screenHeight);
game.batch.draw(player.getCurrentFrame(), player.getBound().x, player.getbound().y, player.getBound().width, player.getBound().height)
game.batch.draw(holdStart, (screenWidth / 2 - (holdStartWidth / 2)), (float) (screenHeight * 0.5), holdStartWidth, holdStartWidth * (hsHeight / hsWidth));
game.batch.end();
}

Android problems with Viewport

When i run the game in Desktop works fine, but when i run it in my android device, the image looks cuted in a half and when i use the PLAY button the game closes, anyone can help me? thank you.
public class GameScreen extends AbstractScreen {
private Viewport viewport;
private Camera camera;
private SpriteBatch batch;
private Texture texture;
private float escala;
private Paddle Lpaddle, Rpaddle;
private Ball ball;
private BitmapFont font;
private int puntuacion, puntuacionMaxima;
private Preferences preferencias;
private Music music;
private Sound sonidoex;
public GameScreen(Main main) {
super(main);
preferencias = Gdx.app.getPreferences("PuntuacionAppPoints");
puntuacionMaxima = preferencias.getInteger("puntuacionMaxima");
music =Gdx.audio.newMusic(Gdx.files.internal("bgmusic.mp3"));
music.play();
music.setVolume((float) 0.3);
music.setLooping(true);
sonidoex = Gdx.audio.newSound(Gdx.files.internal("explosion5.wav"));
}
public void create(){
camera = new PerspectiveCamera();
viewport = new FitViewport(800, 480, camera);
}
public void show(){
batch = main.getBatch();
texture = new Texture(Gdx.files.internal("spacebg.png"));
Texture texturaBola = new Texture(Gdx.files.internal("bola.png"));
ball = new Ball(Gdx.graphics.getWidth() / 2 - texturaBola.getWidth() / 2, Gdx.graphics.getHeight() / 2 - texturaBola.getHeight() / 2);
Texture texturaPala= new Texture(Gdx.files.internal("pala.png"));
Lpaddle = new LeftPaddle(80, Gdx.graphics.getHeight()/2 -texturaPala.getHeight() /2);
Rpaddle = new RightPaddle(Gdx.graphics.getWidth() -100, Gdx.graphics.getHeight()/2 - texturaPala.getHeight() /2, ball);
font = new BitmapFont();
font.setColor(Color.WHITE);
font.setScale(1f);
puntuacion = 0;
}
public void render(float delta){
Gdx.gl.glClearColor(0, 0, 0, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
updatePuntuacion();
Lpaddle.update();
Rpaddle.update();
ball.update(Lpaddle, Rpaddle);
batch.begin();
batch.draw(texture, 0, 0,texture.getWidth(), texture.getHeight());
ball.draw(batch);
Lpaddle.draw(batch);
Rpaddle.draw(batch);
font.draw(batch, "Points: " + Integer.toString(puntuacion), Gdx.graphics.getWidth() / 4 ,Gdx.graphics.getHeight() - 5);
font.draw(batch, "High score: " + Integer.toString(puntuacionMaxima),Gdx.graphics.getWidth() - Gdx.graphics.getWidth() / 4 ,Gdx.graphics.getHeight() - 5);
batch.end();
}
private void updatePuntuacion(){
if(ball.getBordes().overlaps(Lpaddle.getBordes())) {
puntuacion = puntuacion + 1;
if(puntuacion > puntuacionMaxima)
puntuacionMaxima = puntuacion;
}
if(ball.getBordes().x <= 0)
sonidoex.play();
if(ball.getBordes().x <= 0)
puntuacion =0;
if(ball.getBordes().x <=0)
Gdx.input.vibrate(1000);
if(ball.getBordes().x <=0)
Screens.juego.setScreen(Screens.MAINSCREEN);
ball.comprobarPosicionBola();
}
public void hide(){
font.dispose();
texture.dispose();
}
#Override
public void dispose(){
preferencias.putInteger("puntuacionMaxima", puntuacionMaxima);
preferencias.flush();
}
public void resize(int width, int height){
float widthImage = texture.getWidth();
float heightImage = texture.getHeight();
float r = heightImage / widthImage;
if(heightImage > height) {
heightImage = height;
widthImage = heightImage / r;
}
if(widthImage > width) {
widthImage = width;
heightImage = widthImage * r;
}
escala = width / widthImage;
if(Gdx.app.getType()== ApplicationType.Android)
viewport.update(width, height);
}
}
Firstly, use an orthograpic camera.
camera=new OrthographicCamera(800,480);
camera.position.set(800/2f,480/2f,0);
viewport=new FitViewport(800,480,camera);
Now 0,0 is in the left bottom corner of your screen.
And before doing batch.begin don't forget to set your projection matrix
batch.setProjectionMatrix(camera.combined);
batch.begin();
////
////
batch.end();

How to set gravity of Component at center in MT4J framework like the video I am providing the link?

Visit for video (http://www.monstermedia.net/portfolio.php#126)
I want to make the behavior of component like this, if I drag any component and drop anywhere on the screen then it should be return to the center of the screen smoothly like the video. I can set any component at any of specific position on the screen but unable to return them again at center.
The code is
public class Physics extends AbstractScene
{
private float timeStep = 1.0f / 60.0f;
private int constraintIterations = 10;
private int windowWidth=MT4jSettings.getInstance().windowWidth;
private int windowHeight= MT4jSettings.getInstance().windowHeight;
/** THE CANVAS SCALE **/
private float scale = 20;
private AbstractMTApplication app;
public World world;
private MTComponent physicsContainer;
PhysicsRectangle physRect;
PhysicsRectangle borderLeft, borderRight, borderTop, borderBottom;
Vector3D pos;
Vec2 gravity1;
AABB worldAABB;
public Physics(AbstractMTApplication mtApplication, String name)
{
super(mtApplication, name);
this.app = mtApplication;
float worldOffset = 600;
worldAABB = new AABB(new Vec2(-worldOffset, -worldOffset), new Vec2((app.width)/scale + worldOffset, (app.height)/scale + worldOffset));
gravity1 = new Vec2(0, 30);
boolean sleep = false;
//Create the pyhsics world
this.world = new World(worldAABB, gravity1, sleep);
this.registerGlobalInputProcessor(new CursorTracer(app, this));
this.registerPreDrawAction(new UpdatePhysicsAction(world, timeStep, constraintIterations, scale));
physicsContainer = new MTComponent(app);
physicsContainer.scale(scale, scale, 1, Vector3D.ZERO_VECTOR);
this.getCanvas().addChild(physicsContainer);
//Create borders around the screen
this.createScreenBorders(physicsContainer);
//Createa Rectangles
for (int i = 0; i < 5; i++)
{
pos = new Vector3D(windowWidth/2f, windowHeight/2f);
physRect = new PhysicsRectangle(pos, 100, 100, app, world, 1f, 0.4f, 0.4f, scale);
MTColor col = new MTColor(ToolsMath.getRandom(60, 255),ToolsMath.getRandom(60, 255),ToolsMath.getRandom(60, 255));
physRect.setFillColor(col);
physRect.setStrokeColor(col);
PhysicsHelper.addDragJoint(world, physRect, physRect.getBody().isDynamic(), scale);
physicsContainer.addChild(physRect);
}
}
private void createScreenBorders(MTComponent parent)
{
//Left border
float borderWidth = 50f;
float borderHeight = app.height;
Vector3D pos = new Vector3D(-(borderWidth/2f) , app.height/2f);
borderLeft = new PhysicsRectangle(pos, borderWidth, borderHeight, app, world, 0,0,0, scale);
borderLeft.setName("borderLeft");
parent.addChild(borderLeft);
//Right border
pos = new Vector3D(app.width + (borderWidth/2), app.height/2);
borderRight = new PhysicsRectangle(pos, borderWidth, borderHeight, app, world, 0,0,0, scale);
borderRight.setName("borderRight");
parent.addChild(borderRight);
//Top border
borderWidth = app.width;
borderHeight = 50f;
pos = new Vector3D(app.width/2, -(borderHeight/2));
borderTop = new PhysicsRectangle(pos, borderWidth, borderHeight, app, world, 0,0,0, scale);
borderTop.setName("borderTop");
parent.addChild(borderTop);
//Bottom border
pos = new Vector3D(app.width/2 , app.height + (borderHeight/2));
borderBottom = new PhysicsRectangle(pos, borderWidth, borderHeight, app, world, 0,0,0, scale);
borderBottom.setName("borderBottom");
parent.addChild(borderBottom);
}
public void onEnter() {
}
public void onLeave() {
}
}
To run the program use this class, RightClick-> Run as java application
public class staertWaterApp extends MTApplication
{
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
initialize();
}
#Override
public void startUp()
{
addScene(new Physics(this, "Physics Example Scene"));
}
}
What I also tried?
1. I was trying to apply thread but we can not use thread in MT4J (I found this from a website) and the replacement of thread is an interface IPreDrawAction. I applied this but there is another problem that we can not apply this interface on Physics Component, so I used MTComponent, but there is one another problem that we can not apply gravity on MTComponent.

Categories