(May 2021) Monthly WIP Screenshot Thread

Hello everybody,
I wanted to share with you the results of some experiments on moving platforms.

Let’s talk about it after the video:

I tried to model a test level in Blender, I’m not good yet but I do my best.

Below is the list of features visible in the video:

  • Physics with Minie.
  • third-person view demo with automatic camera obstruction detection.
  • the elevator can move up and down between floors.
  • the position of the floors and the speed of the lift are configurable in the controller.
  • the lift doors can be opened by interacting with the call panel, which is activated with a simple proximity trigger.
  • when the player can open the doors, a message appears at the top left.
  • the doors close automatically when the player moves too far from the call panel.
  • all the geometries that are inside the elevator node have a rigid body in kinematic mode (Minie wants a mass > 0).

I have simplified some physics problems that I have not been able to solve. For example when the elevator moves, I disable the BetterCharacterControl and move the player along with the platform with the spatial.move() method. This means that the player cannot move during the ascent and descent of the elevator, while the camera remains free to move at will.
In some Unity tutorials, to resolve this limitation, the player is temporarily attached to the elevator node during movement and then reattached to the scene node. I was unable to imitate this approach in jMonkey.

Here’s the script of the elevator.

    public class ElevatorControl extends AbstractControl implements ActionListener, TriggerListener {

        public BitmapText interactionUI;
        public Node player;
        public Spatial platform;
        public AudioNode elevatorPanelSFX;
        public AudioNode elevatorStopSFX, elevatorMoveSFX;
        public List<Spatial> lstDoors;
        public float ySpeed = 2f;
        
        public Vector3f[] floors = { 
            new Vector3f(0, 0, 0), 
            new Vector3f(0, 14, 0), 
            new Vector3f(0, 28, 0)
        };
        
        /* Constants */
        private int floorNumber = 0;
        private boolean callElevator;
        private boolean onPlatform;
        private boolean isMoving;
        boolean isMoved;
        private String dirName;
        
        private final String MOVE_PLATFORM_UP   = KeyMapping.KEY_1;
        private final String MOVE_PLATFORM_DOWN = KeyMapping.KEY_2;
        private final String OPEN_DOORS         = KeyMapping.KEY_3;

        @Override
        public void onAction(String action, boolean isPressed, float tpf) {
            if (callElevator && !isPressed) {
                if (action.equals(OPEN_DOORS)) {
                    elevatorPanelSFX.play();
                    openDoors();
                }
            }
            
            if (onPlatform && !isMoving && !isPressed) {
                if (action.equals(MOVE_PLATFORM_UP)) {
                    isMoving = true;
                    dirName = "ElevatorUp";
                    ySpeed = Math.abs(ySpeed);
                    floorNumber++;

                } else if (action.equals(MOVE_PLATFORM_DOWN)) {
                    isMoving = true;
                    dirName = "ElevatorDown";
                    ySpeed = -1 * Math.abs(ySpeed);
                    floorNumber--;
                }
            }
        }
        
        @Override
        public void controlUpdate(float tpf) {

            if (isMoving) {
                
                floorNumber = MathUtils.clamp(floorNumber, 0, floors.length - 1);
                Vector3f floor = floors[floorNumber];
                
                boolean moveUp = dirName.equals("ElevatorUp") && platform.getWorldTranslation().getY() < floor.y;
                boolean moveDown = dirName.equals("ElevatorDown") && platform.getWorldTranslation().getY() > floor.y;
                
                if (moveUp || moveDown) {
                    setPlayerInputEnabled(false);
                    //platform.attachChild(player); //this approach doesn't work
                    player.move(0f, ySpeed * tpf, 0f);
                    platform.move(0f, ySpeed * tpf, 0f);
                    closeDoors();

                } else {
                    isMoving = false;
                    Vector3f v = platform.getWorldTranslation().setY(floor.y);
                    platform.setLocalTranslation(v);
                    //rootNode.attachChild(player); //this approach doesn't work
                    setPlayerInputEnabled(true);
                    openDoors();
                }
                
                if (isMoving && !isMoved) {
                    elevatorMoveSFX.play();
                    isMoved = true;
                }
                if (!isMoving && isMoved) {
                    elevatorMoveSFX.stop();
                    elevatorStopSFX.play();
                    isMoved = false;
                }
            }
            
            if (!onPlatform && FVector.distance(platform, player) > 6f) {
                closeDoors();
            }
        }
        
        private void setPlayerInputEnabled(boolean enable) {
            player.getControl(BetterCharacterControl.class).setEnabled(enable);
        }

        private void openDoors() {
            for (Spatial sp : lstDoors) {
                sp.getControl(FlipFlopControl.class).setStatus(FlipFlopControl.Status.FLIP);
            }
        }

        private void closeDoors() {
            for (Spatial sp : lstDoors) {
                sp.getControl(FlipFlopControl.class).setStatus(FlipFlopControl.Status.FLOP);
            }
        }

        @Override
        protected void controlRender(RenderManager rm, ViewPort vp) {
            //To change body of generated methods, choose Tools | Templates.
        }

        @Override
        public void onTriggerEnter(EnterableTrigger trigger) {
            if (trigger.getTagName().equals("Elevator-Platform")) {
                onPlatform = true;
            }
            if (trigger.getTagName().equals("Elevator-Button")) {
                callElevator = true;
                interactionUI.setText("Press SPACE to call the elevator");
            }
        }

        @Override
        public void onTriggerExit(EnterableTrigger trigger) {
            if (trigger.getTagName().equals("Elevator-Platform")) {
                onPlatform = false;
            }
            if (trigger.getTagName().equals("Elevator-Button")) {
                callElevator = false;
                interactionUI.setText("");
            }
        }
    }

Let me know if you liked the demo, write your suggestions and ideas.

Greetings to all, I hope to see your projects soon.

12 Likes

I’m kinda bouncing back and forth between projects right now. I got tired of coding my FPS, so I coded a turn-based puzzle platformer with an interesting mechanic of “bump-bombs”.

This game is much better structured than the other one, and it has better graphical capability since I’ve finally learned basic Blender modelling and UV unwrapping and don’t have to download other people’s models (still a little rough though :wink:).

Tutorial Level

Flow-blown puzzle-y level

All in all, I’m very pleased with how this is turning out. I just need to “juice” it up a bit (like player walking/pushing/being-pushed animations), particles, and a cool sky!

This is the result of 3 days worth of coding, most of which was taken with re-writing the guts of my code to fix physics issues.

As for my FPS that I said I’d release soon, it has some problems with keeping the player attached. In other words, it isn’t nearly satisfying enough to play over and over like other action games. :expressionless:

8 Likes

@capdevon have you searched for “bullet py elevator” (or moving platform) and looked for results on the bullet forum? that’s what I usually do when I’m trying to understand how bullet works :stuck_out_tongue: It might be that you have to write some extra code to make this work.

1 Like

For bullet, moving platforms, elevators, etc. can be done with kinematic objects (mass 0) but you have to set the velocity when you move them. They will then transfer the velocity appropriate to whatever is sitting on them.

The cool thing is that you can set velocity on stuff that isn’t even moving to make conveyor belts and stuff.

That’s how they are done in this old demo from two years ago:

Probably won’t work with CharacterControl since it’s not physics based at all. But it would work with BCC. (That demo doesn’t use either of them… Jaime is a physics object controlled by forces like BCC though.)

5 Likes

I’ve been trying to push One Million Worlds as far as it will go, so as a test I’ve been creating (in game) a missile launcher out of blocks

Far beyond what I’d expect a player to actually do but fun to see what’s possible.

(The way it works is that it has a script block in it controlling its engines based on the sensor output on the front, these are largely used in seeded items to give complex behaviours but they do expose a programming interface in-game if advanced players want to tinker with them)

image

13 Likes

lol, looks fun :smiley:

this very much remind of “From the Depths” Game, where Rockets were builded from block modules like here. (they could have different behaviours/damage/speed/etc)

1 Like

@daBlesr I did a search on https://pybullet.org, but found nothing.

@pspeed it’s an interesting idea. With the Minie library the objects in kinematic mode must have mass > 0. On the other hand, I can access the RigibBody of the BCC with the bcc.getRigidBody() method. Could you point me a link to the source code or paste me a snippet or example of the code showing part of your suggestion?

BCC should not be kinematic. It’s controlled by forces already.

Kinematic objects are usually massless by definition. They are static objects that you move with code. But as such, they have no inherent velocity of their own. This means that when they are colliding with other objects their own velocity is not naturally transferred to the other object (because it has no velocity).

…but you can give kinematic objects a velocity.
https://javadoc.jmonkeyengine.org/v3.3.2-stable/com/jme3/bullet/objects/PhysicsRigidBody.html#setLinearVelocity-com.jme3.math.Vector3f-

It won’t cause them to move… it just makes other objects collide with them as if they are moving. (So a character standing on a kinematic platform will move with the kinematic platform.)

The source code for the Jaime demo in my video is here: SiO2/demos/bullet-char at master · Simsilica/SiO2 · GitHub

It doesn’t use JME controls but manages the bullet RigidBody objects directly based on ES components… so if you are not used to ECS then it may be hard to translate.

1 Like

Afaik this won’t work with BCC either, as it directly modifies the velocity. But you need to test it yourself, I might be wrong.

This might help

Regarding the SiO2 bullet-char demo, you can take a look at CharInputDriver to see how it translates the character input to a force and apply it to PhysicsRigidBody

1 Like

Boo… fake physics again. I guess we need a better better character control. :confused:

Added some frayed edge shapes… cool for making peasant clothes or tassles and stuff:

In general, I spend way too much time goofing around in the editor when I can’t even save clothing yet…

image

9 Likes

I modeled a very futuristic looking computer. Once it’s picked up it enables the HUD via its visor and lets players read data logs and technical manuals found around the ship.


12 Likes

I got a very primitive saving and loading working this weekend so I was able to start tweaking some visual issues that annoyed me. Mainly that the autogenerated edge normals didn’t ‘read’ well from a distance or in various lighting conditions.

It doesn’t do much good if players can create their own clothing with a variety of fabrics/materials if it all looks flat-shaded anyway.

It’s “good enough” to move on to other things now. Though I will have to finalize a set of textures and a palette of colors for the fabrics and pick a variety of leather colors. That will be a fun project for a different Sunday afternoon.

Here we see leather vest, bleached linen shirt, suede pants (because it was the only non-gawdy color for pants), and dark leather boots + belt. Metal buckles, etc… Each item of clothing was built in the tool on the model and is fully layered so that can be removed, etc… right down to his white underwear (also created in the tool).



6 Likes

I wasn’t going to do anything more with clothing for a while but then I saw an Adam Savage video about fake chainmail using laser-cut EVA foam and I remembered about chainmail!

Once it was in my head, I couldn’t escape it.

So I popped open photoshop and made some ‘height map’ style loops… turned it into a repeating pattern:

Duplicated that a few hundred times into an 8192x8192 texture before turning it into a normal map and crunching it back down to 1024x1024.

And it works pretty well:

Even so ridiculously crunched, it still looks like chainmail even closer than any player will ever get to another mob:

That’s basically all normal map and PBR.

I posted a short video to twitter:

In there I move the lighting around and stuff. You can also see the material in motion which because of the moire effect gives it kind of a sparkly shimmer. (Which for any other material I would hate but it kind of works here, I think.)

Anyway, back to working non-clothing stuff. Though sometime this week I may make a video showing how this crude clothing editor works.

16 Likes

Hi there, continuing the work of providing a high performance compatibility of jme android with android views & Ui, i have built UiPager class that extends android.widget.GridLayout, now i can layout different types of Adapters :

  • Lay out a list of the same Ui-Components, with different data models
  • Lay out a list of different UiStates using UiStateManager class, where each UiState represent an xml layout file & a couple of Ui-Components as well or a couple of other layouts
  • Lay out a list of different xml layouts together directly using a dynamic data model without the end of UiStateManager, Hybrid Lists
  • Traverse through the tree of the Pager or GridLayout
  • Insert a UiComponent or a complex Layout at any position assuming that the position is already exists or using SEQUENTIAL_ADD
  • Lay out the Component list in LinearLayout Style or a GridLayout ie, Columns/Rows style
  • Animation of Ui-Components inside the Ui-Pager could be applied using LayoutTransition, AnimationUtils, ViewPropertyAnimator , …etc.
  • Realtime changes between grid & linear styles could be applied (need to be refreshed to recalculate the boundaries)
  • Adding data to the child Layouts laid inside the GridLayout using just for-loops & a DataModel class which is cool & yet very easy to use, for multiple traversal trees → use multi-dimensional arrays/for-loops, in real android world, to do a list you will need to make a RecyclerView & use its subclasses like Holder that holds UiStates, Adapter that holds the layout enclosing the views, kinda of complex stuff to reach a simple thing…& you cannot do a hybrid list easily…

Example Code :

 //uiPager starts here......
            UiPager uiPager = new UiPager(menu.getContext());
            LayoutTransition layoutTransition = new LayoutTransition();
            Animator animator = ObjectAnimator.ofPropertyValuesHolder(PropertyValuesHolder.ofFloat("translateX", uiPager.getLayoutParams().width, 0),
                    PropertyValuesHolder.ofFloat("translateY", uiPager.getLayoutParams().height, 0));
            animator.setDuration(300);
            animator.setStartDelay(100);
            animator.setInterpolator(new AccelerateInterpolator());
            layoutTransition.setAnimator(LayoutTransition.CHANGE_DISAPPEARING, animator);
            uiPager.setLayoutTransition(layoutTransition);
            ScrollView scrollableContainer = uiPager.initializeScrollableContainer();
            scrollableContainer.setHorizontalScrollBarEnabled(true);
            scrollableContainer.setId(SCROLLABLE_CONTENT);
            menu.addView(scrollableContainer);
            //add-remove-update-integrate-disintegrate views
            invalidateUiPager(gridOn);
 protected void invalidateUiPager(boolean gridOn){
        UiPager uiPager = (UiPager) ((ScrollView)uiStateManager.getChildUiStateById(SCROLLABLE_CONTENT)).getChildAt(0);
        TriggerModel[] model = new TriggerModel[]{
                new TriggerModel("Scale Track", TriggerID.simpleTrackTrigger),
                new TriggerModel("Basic Armature", TriggerID.basicArmatureTrigger),
                new TriggerModel("Stack Loops", TriggerID.stackLoopsTrigger),
                new TriggerModel("Basic Tween", TriggerID.basicTween),
                new TriggerModel("Emitter Tween", TriggerID.emitterTweenTrigger),
                new TriggerModel("Bottle Fall", TriggerID.blendableAnimTrigger),
                new TriggerModel("Blender Anim", TriggerID.blenderTweenTrigger),
                new TriggerModel("Composite", TriggerID.animLayersTrigger),
                new TriggerModel("Blank Field 0", 'B'),
                new TriggerModel("Blank Field 1", 'B'),
                new TriggerModel("Close Menu", TriggerID.closeTrigger),
        };
        if(uiPager.hasUiStates()){
            uiPager.detachAllUiStates();
        }
        if(gridOn){
            uiPager.setColumnCount(3);
            uiPager.setRowCount(3);
        }else{
            uiPager.setColumnCount(1);
            uiPager.setRowCount(1);
        }
       //the powered by jme poster
        RelativeLayout topUpMenu = (RelativeLayout) uiStateManager.fromXML(R.layout.top_up_menu);
        topUpMenu.setLayoutParams(new GridLayout.LayoutParams(GridLayout.spec(0, uiPager.getColumnCount()),
                GridLayout.spec(0, uiPager.getRowCount())));
        topUpMenu.findViewById(R.id.container).findViewById(R.id.dismissForever).setOnClickListener(this);
        uiPager.attachUiState(topUpMenu ,UiPager.SEQUENTIAL_ADD);
       //other UiStates in UiPager
        for (TriggerModel triggerModel : model) {
            RelativeLayout relativeLayout = (RelativeLayout) uiStateManager.fromXML(R.layout.menu_trigger);
            relativeLayout.setPadding(5,5,5,5);
            Button button = relativeLayout.findViewById(R.id.trigger);
            //relativeLayout.getPaddingEnd()*(uiPager.getColumnCount()-1) is the total length of the padding between the buttons only (excluding the sides, that's why (-1)), this number is zero when ColumnCount is 1
            button.setLayoutParams(new RelativeLayout.LayoutParams(displayMetrics.widthPixels/uiPager.getColumnCount() - relativeLayout.getPaddingEnd()*(uiPager.getColumnCount()-1),
                    menu.getLayoutParams().height/4));
            button.setText(triggerModel.getBtnText());
            button.setId(triggerModel.getBtnID());
            button.setOnClickListener(AnimationFactory.this);
            //set the red color for the close menu button ONLY
            if(triggerModel.getBtnID() == TriggerID.closeTrigger){
                button.setBackgroundColor(Color.RED);
            }
            uiPager.attachUiState(relativeLayout, UiPager.SEQUENTIAL_ADD);
        }
        uiPager.attachUiState(topUpMenu ,0);
        uiPager.invalidate();

    }

whenever, you want to update a change in the dataModel or the Ui-Component properties call invalidateUiPager

UiStateManager/UiPager Jme Animaion Tween Hybrid List Linear Style

Example repo :

Source code (docs still not completed tho) :

My wish list :

  • Build a PopChat & MessageBubble Ui-Component, for easy to use friend-friend talks in online games
  • Build a Chronometer for step-up counters & step-down ones, may be we can use this later with BaseAppStates as a frame timer…in time based gameLoops.
  • TCP/IP Communication using InetAddress, as users could play games as local hosts on the same current network (i have built it, but stuck due to android permissions).
11 Likes

This weekend I switched gears back to the core engine to make a push to get all of the original block types implemented. (I’m even using a spreadsheet to keep track!)

Here are all of the main solid block shapes without rotations: (77 block shapes with rotations)

And most of them places around in all available rotations in one place or another:

(found a normal bug in one of the shapes that way… fixed in that image.)

All of those shapes are available in MOSS so will be part of the code when finally published (available as early access to patrons). Next is to add the ability to load custom factories into the shaper tool so that I can finish out the custom Mythruna-specific templates for thatch, plants, fire, etc… ie: the ones that rely on specific assets.

12 Likes

Interesting. can you post a sample video?

1 Like

You can find a demo app here on git :

Currently, I am working on Searching through DataModel, sorting the DataModel…of the UiPager UiStates

7 Likes

I did some advanced camera system stuff, pretty satisfying :slight_smile:
The camera bounding boxes are stackable. A camera bounding box holds a specific type of camera. At the moment a windowed follow camera and a centered camera. The bounding boxes have an order number the lowest one is 0.

12 Likes