DynamicAnimControl with CharacterControl conflict of

I may have used DynamicAnimControl by mistake

I have a CharacterControl to implement character movement

I want to complete some functions of IK That’s what I want to achieve
But I found that if I use CharacterControl and DynamicAnimControl at the same time
The CharacterControl capsule will force DynamicAnimControl out of the position where it shouldn’t be


Normally the DynamicAnimControl should overlap the model

I knew I must have used them incorrectly
But I didn’t know what was wrong. I looked through the documents and didn’t find the answer. What did I miss?

It’s possible to switch back and forth between CharacterControl and DynamicAnimControl using setEnabled(). However, they aren’t meant to be enabled on the same model at the same time.

1 Like

I’m trying to create two different physical Spaces and put them in different physical worlds and I don’t know if it’s going to work

Thank you for your reply

I added a new physical space The DynamicAnimControl runs on this as expected.

But I want to access my newly added physical space and I don’t know how to do that, okay.
I usually use this code to create physical Spaces.

 bulletAppState = app.getStateManager().getState(BulletAppState.class);

Now I’ve created a physical space using new

bulletAppStateDynamic= new BulletAppState();

How do I make this BulletAppState accessible to another BaseAppState.

Hmm, suppose we use CharacterControl for movement and want DAC for adding dynamic physics animation for some joints like long hair or tail dynamics. Is this a valid use case to use both simultaneously?

Are you adding both controls on the same spatial? What if you put CharacterControl on a parent node? (i.e create a new node and attach the character model to it and add CharacterControl to this new node)

Not sure if this is a good way or if it even will work but anyway I am interested to see how it will turn out. (though in my game I am thinking of doing a similar thing, the server has its own physics space and I use a custom character control based on PhysicsRigidBody that deals with movement and I will also have a client-side physics space that utilizes DAC just for dynamic anim effects like dynamic hair,… not sure how it will turn out;)

You need to set an id to each bullet appstate and access it with its id using stateManager.stateForId() instead.

setId method is protected, so you need to extend BulletAppstate class. For example something like:

public class NamedBulletAppState extends BulletAppState {
   public NamedBulletAppState (String name) {
        super();
        setId(name);
   }
}
1 Like

Only if it actually works. Does it?

1 Like

Though if you extend bullet app state then you can have one still be BulletAppState and the other be CustomBulletAppState and then still look them up with getState(Class).

Someday we should retrofit ID-based constructors to all of the JME provided app states.

From a BaseAppState subclass, you need only call the ID-based getState(id, Class) method.

1 Like

I tried it and the results were the same!

This would probably work but I would think it could also significantly lower the framerate if you have the world represented in two BulletAppStates causing the physics engine to do double the work, wouldn’t it?

I don’t use CharacterControl for my game anymore, so I’m just speculating here, but is there any simple way to make physics bodies in the same PhysicsSpace ignore collisions with each other?

If that is not possible, then I would suggest using something else instead of CharacterControl. I typically advise jme users avoid CharacterControl and BetterCharacterControl for a handful of unrelated reasons, and this seems like it might be another reason to add to that list.

I’ve heard discussion from other threads talking about using a GhostControl to detect collisions for character movement, and I personally use JME’s basic collidables like the Ray which is even faster. Both of these solutions lack a physics body (unlike CharacterControl) so they would be able to work cleanly with DAC.

2 Likes

That’s what I wanted to ask and thank you for bringing that up

I’ve only been using JME for about 6 months and I saw a tutorial that taught me how to use CharacterControl to move and that’s why I used it this way. I don’t know all about it yet :wink:

It’s a good suggestion that I might be looking for something else instead of CharacterControl

I need the CharacterControl work cleanly with DAC…
Any tips and suggestions. :wink: :wink: :wink:

(I don’t know if I can express the meaning to you, The translator is not that accurate :pensive: :pensive:)

Have not tried it myself.

As already suggested try to disable collision on the CharacterControl vs the DynamicAnimControl and see if it fixes your issue.

PhysicsRigidBody[] rigidBodies = dynamicAnimControl.listRigidBodies()

for (PhysicsRigidBody body : rigidBodies) {
      characterControl.getCharacter().addToIgnoreList(body);
}

More details on the Minie wiki page:Managing collisions :: The Minie project

2 Likes

video
Movement problem after use

dac.setRagdollMode();

use dynamic mode Movement back to normal

I don’t know why. Can you give me a hint?

I think that is the expected behavior because setRagdollMode makes all links dynamic (controlled by physics) and applies gravity.

I would suggest keeping TorsoLink as kinematic (unaffected by physics) and see if it will make any difference.

So instead of dac.setRagdollMode() use this:

for (BoneLink boneLink : dac.getBoneLinks()) {
     boneLink.setRagdollMode();
}

for (AttachmentLink link : dac.listAttachmentLinks()) {
    link.setRagdollMode();
}

for (IKJoint joint : dac.listIKJoints()) {
     joint.setRagdollMode();
}

I mean CharacterControl doesn’t move properly
It feels like CharacterControl is dragging DynamicAnimControl along
(I may have misexpressed myself)
(Thank you for your patient guidance)

dac.getBoneLinks()//GetBoneLinks () is protected access control in DacLinks
dac.listAttachmentLinks()//ListAttachmentLinks () is protected access control in DacLinks
joint.setRagdollMode();//SetRagdollMode () is not public in IKJoint; It cannot be accessed from an external package

1 Like

Ah, I see now. Sorry for my misunderstanding. I am not sure what causes that.

1 Like
package net.jmecn;

import com.jme3.anim.AnimClip;
import com.jme3.scene.debug.Arrow;
import com.jme3.input.ChaseCamera;
import com.jme3.anim.AnimComposer;
import com.jme3.anim.Armature;
import com.jme3.anim.SkinningControl;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.plugins.HttpZipLocator;
import com.jme3.asset.plugins.ZipLocator;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.animation.DynamicAnimControl;
import com.jme3.bullet.animation.LinkConfig;
import com.jme3.bullet.animation.RangeOfMotion;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.collision.shapes.CapsuleCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.control.CharacterControl;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.collision.shapes.HullCollisionShape;
import com.jme3.bullet.objects.PhysicsRigidBody;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.input.KeyInput;
import com.jme3.input.CameraInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.light.AmbientLight;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.scene.shape.Sphere.TextureMode;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;
import com.jme3.scene.control.CameraControl.ControlDirection;
import com.jme3.scene.CameraNode;
import com.jme3.scene.plugins.bvh.BoneMapping;
import com.jme3.scene.plugins.bvh.SkeletonMapping;
import com.jme3.ui.Picture;
import java.io.File;
import jme3utilities.wes.AnimationEdit;
/**
 * 按键发射小球轰击砖墙。
 * 
 * @author yanmaoyuan
 *
 */
public class CharacterControlDemo extends SimpleApplication implements ActionListener {

    /**
     * 开火,发射小球。鼠标左键触发。
     */
    public final static String FIRE = "fire";
    public final static String FORWARD = "forward";
    public final static String BACKWARD = "backward";
    public final static String LEFT = "left";
    public final static String RIGHT = "right";
    public final static String V = "v";
    public final static String JUMP = "jump";
    public int v1= 0;
    //方向控制器
    private CharacterControl player;
    private Vector3f walkDirection = new Vector3f();
    private Vector3f walkDirectioni = new Vector3f();
    private boolean left = false, right = false, up = false, down = false, v=false;
    /**
     * 显示或隐藏BulletAppState的debug形状。按空格键触发。
     */
    public final static String DEBUG = "debug";
 // 临时变量,用于保存摄像机的方向。避免在simpleUpdate中重复创建对象。
    private Vector3f camDir = new Vector3f();
    private Vector3f camLeft = new Vector3f();
    /** 砖块的尺寸 */
    private static final float brickLength = 0.48f;
    private static final float brickWidth = 0.24f;
    private static final float brickHeight = 0.12f;
    private AnimComposer control;
    private BulletAppState bulletAppState;
    private Node character;
    private ChaseCamera chaseCam;
    private CameraInput cameraInput;
  private  DynamicAnimControl dac;
    @Override
    public void simpleInitApp() {

        createArrow(new Vector3f(5, 0, 0), ColorRGBA.Green);
        createArrow(new Vector3f(0, 5, 0), ColorRGBA.Red);
        createArrow(new Vector3f(0, 0, 5), ColorRGBA.Blue);
    
//        cam.setLocation(new Vector3f(0, 4f, 6f));
//        cam.lookAt(new Vector3f(2, 2, 0), Vector3f.UNIT_Y);

        bulletAppState = new BulletAppState();
        stateManager.attach(bulletAppState);
        bulletAppState.setDebugEnabled(true);
        // 初始化按键
        initKeys();

        // 初始化光照
        initLight();

        // 初始化场景
        initScene();

    }
 /**
     * 创建一个箭头
     * 
     * @param vec3  箭头向量
     * @param color 箭头颜色
     */
    private void createArrow(Vector3f vec3, ColorRGBA color) {
        // 创建材质,设定箭头的颜色
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        mat.setColor("Color", color);

        // 创建几何物体,应用箭头网格。
        Geometry geom = new Geometry("arrow", new Arrow(vec3));
        geom.setMaterial(mat);

        // 添加到场景中
        rootNode.attachChild(geom);
    }
    @Override
    public void onAction(String name, boolean isPressed, float tpf) {

 if (DEBUG.equals(name) && isPressed) {
     
            boolean debugEnabled = bulletAppState.isDebugEnabled();
            bulletAppState.setDebugEnabled(!debugEnabled);
        } else if (LEFT.equals(name)) {
            left = isPressed;
        if (isPressed) {
        System.err.println("按下W");           
        control.setCurrentAction("Walk");
       control.setGlobalSpeed(1f);             
       } else {
        System.err.println("弹起W");
        control.setCurrentAction("Stand");
        control.setGlobalSpeed(1f);
        }
        } else if (RIGHT.equals(name)) {
            right = isPressed;
                    if (isPressed) {
        System.err.println("按下W");           
        control.setCurrentAction("Walk");
       control.setGlobalSpeed(1f);             
       } else {
        System.err.println("弹起W");
        control.setCurrentAction("Stand");
        control.setGlobalSpeed(1f);
        }
        } else if (FORWARD.equals(name)) {
            up = isPressed;
                    if (isPressed) {
        System.err.println("按下W");           
        control.setCurrentAction("Walk");
       control.setGlobalSpeed(1f);             
       } else {
        System.err.println("弹起W");
        control.setCurrentAction("Stand");
        control.setGlobalSpeed(1f);
        }
        } else if (BACKWARD.equals(name)) {
            down = isPressed;
                    if (isPressed) {
        System.err.println("按下W");           
        control.setCurrentAction("Walk");
       control.setGlobalSpeed(1f);             
       } else {
        System.err.println("弹起W");
        control.setCurrentAction("Stand");
        control.setGlobalSpeed(1f);
        }
        }else if(V.equals(name)) {
            if(!isPressed){
          switch(v1){
        case 0 :
        v1++;chaseCam.setLookAtOffset(camDir.add(0f, 1.5f, 0f)); v1=0;
   
       break; //可选
          case 1 :
        chaseCam.setLookAtOffset(camDir.add(0f, 1.5f, -2.5f).add(camLeft)); v1=0;
     
       break; //可选
    //你可以有任意数量的case语句
    default : //可选
       //语句
          }
                

              

          }
             
            v = isPressed;
        }else if (JUMP.equals(name) && isPressed) {
            if(player.onGround()==true){
                player.jump();
      
            }
            
            	System.err.println(player.onGround());
           
        }
 
    }

    /**
     * 初始化按键输入
     */
    private void initKeys() {
        inputManager.addMapping(FIRE, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        inputManager.addMapping(DEBUG, new KeyTrigger(KeyInput.KEY_F1));
        inputManager.addMapping(LEFT, new KeyTrigger(KeyInput.KEY_A));
        inputManager.addMapping(RIGHT, new KeyTrigger(KeyInput.KEY_D));
        inputManager.addMapping(FORWARD, new KeyTrigger(KeyInput.KEY_W));
        inputManager.addMapping(BACKWARD, new KeyTrigger(KeyInput.KEY_S));
        inputManager.addMapping(JUMP, new KeyTrigger(KeyInput.KEY_SPACE));
        inputManager.addMapping(V, new KeyTrigger(KeyInput.KEY_V));
        inputManager.addListener(this, FIRE, DEBUG,LEFT,RIGHT,FORWARD,BACKWARD,JUMP,V);
        
        
    }

    /**
     * 初始化光照
     */
    private void initLight() {
        // 环境光
        AmbientLight ambient = new AmbientLight();
        ambient.setColor(new ColorRGBA(0.3f, 0.3f, 0.3f, 1f));

        // 阳光
        DirectionalLight sun = new DirectionalLight();
        sun.setDirection(new Vector3f(-1, -2, -3).normalizeLocal());

        rootNode.addLight(ambient);
        rootNode.addLight(sun);
    }

    /**
     * 初始化场景
     */
    private void initScene() {
        model();
        makeFloor();
     
   
        
    }

    /**
     * 制作地板
     */
    private void makeFloor() {
        // 网格
        float radius = 100f;// 胶囊半径0.3米
        float height = 0.1f;// 胶囊身高1.8米
        float stepHeight = 100f;// 角色步高0.5米
        Box floor = new Box(radius, height, stepHeight);
        floor.scaleTextureCoordinates(new Vector2f(3, 6));

        // 材质
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        Texture tex = assetManager.loadTexture("Interface/pic.png");
        tex.setWrap(WrapMode.Repeat);
        mat.setTexture("ColorMap", tex);

        // 几何体
        Geometry geom = new Geometry("floor", floor);
        geom.setMaterial(mat);
        geom.setLocalTranslation(0, -1.8f, 0);// 将地板下移一定距离,让表面和xoz平面重合。

        // 刚体
        RigidBodyControl rigidBody = new RigidBodyControl(0);
        geom.addControl(rigidBody);
        rigidBody.setCollisionShape(new BoxCollisionShape(new Vector3f(radius, height, stepHeight)));
            rigidBody.setFriction(10f);
        rootNode.attachChild(geom);
        bulletAppState.getPhysicsSpace().add(rigidBody);
    }


    
        private void model() {
        float radius = 0.5f;// 胶囊半径0.3米
        float height = 3.3f;// 胶囊身高1.8米
        float stepHeight = 0.5f;// 角色步高0.5米
            //模型
             File file = new File("lina.zip");
                if (file!=null) {
            assetManager.registerLocator(
                    "https://www.icyboxs.com/icyboxs/lina.zip",
                    HttpZipLocator.class);
        } else {
            assetManager.registerLocator("lina.zip", ZipLocator.class);
        }
       character = new Node("Character");
            Node  model =  (Node)assetManager.loadModel("Walk.gltf");
            Node  Stand =  (Node)assetManager.loadModel("Stand.gltf");
            model.move(0, -(height/2+radius), 0);
            //model.scale(1.8f);
            AnimComposer StandControl=Stand.getChild(0).getControl(AnimComposer.class);
            character.attachChild(model);// 挂到角色根节点下
            rootNode.attachChild(character);
            control = model.getChild(0).getControl(AnimComposer.class);
            control.setCurrentAction("Walk");
            control.setGlobalSpeed(1f);
              // AnimClip newClip = AnimUtils.retargetClip(control1.getAnimClip("Walk"), model, false, "Walk");
       SkeletonMapping skeletonMapping=new SkeletonMapping();
       SkinningControl modelSkinningControl = ((Node)model).getChild(0).getControl(SkinningControl.class);
       SkinningControl WalkSkinningControl = ((Node)Stand).getChild(0).getControl(SkinningControl.class);
       Armature  armature = modelSkinningControl.getArmature();
       Armature  armature1 = WalkSkinningControl.getArmature();
       modelSkinningControl.getArmature().getJointList().forEach(joint -> 
       skeletonMapping.addMapping(new BoneMapping(joint.getName(), joint.getName())));
       AnimClip newClip1 = AnimationEdit.retargetAnimation(StandControl.getAnimClip("Stand"), armature1,armature , skeletonMapping, "Stand");
     // retargetAnimation(control1.getAnimClip("Walk"), Walk, model, skeletonMapping, "Walk");
       control.addAnimClip(newClip1);

                LinkConfig defaultConfig = new LinkConfig();
        RangeOfMotion defaultRom = new RangeOfMotion(1f);
          dac = new DynamicAnimControl();

       dac.link("Bip001-Head", defaultConfig, defaultRom); // right elbow
       dac.link("Bip001-R-Thigh", defaultConfig, defaultRom); // right elbow
       dac.link("Bip001-L-Thigh", defaultConfig, defaultRom); // right elbow
       dac.link("Bip001-R-Calf", defaultConfig, defaultRom); // right elbow
       dac.link("Bip001-L-Calf", defaultConfig, defaultRom); // right elbow
      
         dac.link("Bip001-R-UpperArm", defaultConfig, defaultRom); // right elbow
         dac.link("Bip001-L-UpperArm", defaultConfig, defaultRom); // right elbow
         dac.link("Bip001-L-Forearm", defaultConfig, defaultRom); // right elbow
         dac.link("Bip001-R-Forearm", defaultConfig, defaultRom); // right elbow
         dac.link("Bip001-Spine", defaultConfig, defaultRom);
       dac.link("ShotSkirt_04_Bone", defaultConfig, defaultRom); // right wrist
       dac.link("ShotSkirt_03_Bone", defaultConfig, defaultRom); // right wrist
       dac.link("ShotSkirt_01_Bone", defaultConfig, defaultRom); // right wrist
       dac.link("ShotSkirt_02_Bone", defaultConfig, defaultRom); // right wrist
      //  NOTE: Complete configuration BEFORE adding control to a model.
      //  ninjaModel.addControl(dac);

        model.getChild(0).addControl(dac); 
 
        dac.setPhysicsSpace(bulletAppState.getPhysicsSpace());//Dynamic
 
        // 使用胶囊体作为玩家的碰撞形状
        CapsuleCollisionShape capsuleShape = new CapsuleCollisionShape(radius, height, 1);
        // 使用CharacterControl来控制玩家物体
        player = new CharacterControl(capsuleShape, stepHeight);
        player.setJumpSpeed(10f);// 起跳速度
        player.setFallSpeed(20f);// 坠落速度
        player.setGravity(9.8f * 3);// 重力加速度

        character.addControl(player);
       	
        bulletAppState.getPhysicsSpace().add(player);
       for (PhysicsRigidBody body :  dac.listRigidBodies()) {
              player.getCharacter().addToIgnoreList(body);
        }
        chaseCam = new ChaseCamera(cam, character, inputManager);

           inputManager.deleteMapping(CameraInput.CHASECAM_ZOOMOUT);
           //to disable zoom in
           inputManager.deleteMapping(CameraInput.CHASECAM_ZOOMIN);
           flyCam.setEnabled(false);
          chaseCam.setLookAtOffset(new Vector3f(0, 1.5f, 0));
          

        chaseCam.setDragToRotate(false); //把鼠标锁定在窗口里

        chaseCam.setMinVerticalRotation(-1.570625f);

        }
        
        /**
         主循环
         **/
        
    @Override
    public void simpleUpdate(float tpf) {
        inputManager.setCursorVisible(false);
        camDir.set(cam.getDirection()).multLocal(0.1f);
        camLeft.set(cam.getLeft()).multLocal(0.1f);
        camDir.y = 0;
        camLeft.y = 0;
        walkDirection.set(0, 0, 0);
        
        // 计算运动方向
        boolean changed = false;
        if (left) {
            walkDirectioni.addLocal(camLeft);
            walkDirection.addLocal(camLeft);
            
            changed = true;
        }
        if (right) {
            walkDirectioni.addLocal(camLeft.negate());
           
            walkDirection.addLocal(camLeft.negate());
           
            changed = true;
        }
        if (up) {
            walkDirectioni.addLocal(camDir);
        
            walkDirection.addLocal(camDir);
      
            changed = true;
        }
        if (down) {
            walkDirectioni.addLocal(camDir.negate());
        
            walkDirection.addLocal(camDir.negate());
            
            changed = true;
        }
        if (v) {
           // player.setViewDirection(walkDirectioni);
            if(v){
                	  
            }else{
                   
            }
          // System.err.println(); 
            changed = true;
        }
        if (changed) {
             
            walkDirectioni.y = 0.0f;// 将行走速度的方向限制在水平面上。
            walkDirectioni.normalizeLocal();// 单位化
            walkDirectioni.multLocal(3f);// 改变速率
            walkDirection.y = 0;// 将行走速度的方向限制在水平面上。
            walkDirection.normalizeLocal();// 单位化
            walkDirection.multLocal(0.3f);// 改变速率
            
        }
        if (walkDirection.length() != 0f) {
             player.setViewDirection(walkDirection);
        }

         chaseCam.setDefaultDistance((-chaseCam.getVerticalRotation()/-1.57f-1f)*5f);


     	
       player.setWalkDirection(walkDirection);

    }
    
    
    /**
     * 在指定位置放置一个物理砖块
     * 
     * @param loc
     *            砖块的位置
     */
    private void makeBrick(Vector3f loc) {
        // 网格
        Box box = new Box(brickLength, brickHeight, brickWidth);
        box.scaleTextureCoordinates(new Vector2f(1f, .5f));

        // 材质
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        Texture tex = assetManager.loadTexture("Interface/pic.png");
        mat.setTexture("ColorMap", tex);

        // 几何体
        Geometry geom = new Geometry("brick", box);
        geom.setMaterial(mat);
        geom.setLocalTranslation(loc);// 把砖块放在指定位置

        // 刚体
        
        RigidBodyControl rigidBody = new RigidBodyControl(2f);
        geom.addControl(rigidBody);
        rigidBody.setCollisionShape(new BoxCollisionShape(new Vector3f(brickLength, brickHeight, brickWidth)));

        rootNode.attachChild(geom);
        bulletAppState.getPhysicsSpace().add(rigidBody);
    }

    /**
     * 从摄像机所在位置发射一个小球,初速度方向与摄像机方向一致。
     */
    private void shootBall() {
        // 网格
        Sphere sphere = new Sphere(32, 32, 0.4f, true, false);
        sphere.setTextureMode(TextureMode.Projected);

        // 材质
        Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
        Texture tex = assetManager.loadTexture("Interface/pic.png");
        mat.setTexture("ColorMap", tex);

        // 几何体
        Geometry geom = new Geometry("cannon ball", sphere);
        geom.setMaterial(mat);

        // 刚体
        RigidBodyControl rigidBody = new RigidBodyControl(1f);
        geom.addControl(rigidBody);
        rigidBody.setCollisionShape(new SphereCollisionShape(0.5f));
        rigidBody.setPhysicsLocation(cam.getLocation());// 位置
        rigidBody.setLinearVelocity(cam.getDirection().mult(50));// 初速度
      
        rootNode.attachChild(geom);
        bulletAppState.getPhysicsSpace().add(rigidBody);
    }

    public static void main(String[] args) {
        CharacterControlDemo app = new CharacterControlDemo();
        app.start();
    }
}

I can’t really describe my problem, but this is a demo that requires Minie, you can create a class and get started

1 Like
     bulletAppState = app.getStateManager().getState("Minie",newBulletAppState.class);
     DACbulletAppState = app.getStateManager().getState("DAC",newBulletAppState.class);

This is one of my attempts to use multiple BulletappStates to make

So far so good

Two physical Spaces are created

Everything seems to be fine and we need to make further attempts

https://youtu.be/fAmPM0esZxs

Thanks again for your help

1 Like

You’re very welcome. Glad to hear it works with multiple physics spaces.

But I really hope there would be a way to make DAC works with CharacterControl or BetterCharacterControl or PhysicsRigidBody simultaneously in a single physics space as well and IMO there is a pretty good use case for that.

1 Like