Skip to content

Terra, Phase 4

26 to 50 of 180

Exactly this^ It doesn't matter how you do the actual animation (keyframe or specify motion in the script). You can use the getHarnessState() method of the "Train" class to tie the animation directly to the train's harnesses. The method simply outputs a value between 0 (fully closed) and 1 (fully open). It should be relatively easy to integrate into the existing script that attaches the monorail to the train.

So I can keyframe animate the doors no problem, but how would I go about that? Would I just animate the door opening, or have the animation open, wait, and then close the doors again?

Okay, so if I animate the doors, what exactly would I need to add to the script for it to activate when the dummy train restraints open/close?

This is the script I'm using here:

import com.nolimitscoaster.*;
import nlvm.math3d.*;


public class Monorail1 extends Script
{
    private static final float xOffset = 0; // left/right
    private static final float yOffset = 1.5f; //up/down
    private static final float zOffset =  0.0f; //back/forth
    private static final float range = 5;   // The first train within this range of the scene object will receive the attachment. (range is in meters)
    private static final int carToAttach = 0; // Defines the car on the train that will receive the attachment. (0 = lead car or zero car)
    
    private SceneObject sco;    // Scenery object handle.
    private Train train;    // Train handle.
    private static final int trainToAttach = 1;
    private Vector3f posOut = new Vector3f(0,0,0);  // Will store the scenery object's position based on the car's position.
    private Vector3f pitchHeadBankOut = new Vector3f(0,0,0);    // Will store the scenery object's orientation based on the car's orientation.
    private Matrix4x4f carMatrix = new Matrix4x4f();    // Will store the car's orientation.
    
    // float xOffset_cam1 = -2;
    // float yOffset_cam1 = 0.0f;
    // float zOffset_cam1 = 0.0f;
    
    private static final float warpRange = 4;
    private static final int numSeats = 12;
    ExternalRideView pov;
    
    public bool onInit()
    {                                   
        sco = sim.getSceneObjectForEntityId(getParentEntityId());
        if (sco == null)
        {
            System.err.println("Monorail1.nlvm: This script must be assigned to a scene object.");
            return false;
        }
        
        TrackPos trackPos = sim.findNearestCoasterTrack(sco.getTranslation(), range);
        if (trackPos == null)
        {
            System.err.println("Monorail1.nlvm: No track found within range of " + range + " meters.");
            return false;
        }
        
        Coaster coaster = trackPos.getCoaster();
        train = coaster.findNearestTrain(sco.getTranslation(), range);
        if (train == null)
        {
            System.err.println("RaftingCar.nlvm: No train found within range of " + range + " meters.");
            return false;
        }
        
        pov = sim.createExternalRideView();
      pov.setEnterWarpPoint(sco.getTranslation(), warpRange);
      pov.setEnterWarpPointEnabled(true);
      pov.setLabel("Enter Skytrain Front Cab");
        
        return true;
    }

    Matrix4x4f matrixCamera = new Matrix4x4f();
    
    public void onNextFrame(float tick)
    {
        pov.setEnterWarpPoint(sco.getTranslation(), warpRange);
        train.getCarMatrix(carToAttach, carMatrix);
        Tools.matrixToPitchHeadBankPos(carMatrix, pitchHeadBankOut, posOut);
        Matrix4x4f matrix = new Matrix4x4f();
        matrix.initTrans(xOffset, yOffset, zOffset);
        carMatrix.multRight(matrix);
        Tools.matrixToPitchHeadBankPos(carMatrix, pitchHeadBankOut, posOut);
        sco.setRotation(pitchHeadBankOut);
        sco.setTranslation(posOut);
    
        if (pov.isActive())
        {
            int curCar = pov.getCurrentSeatIndex(numSeats);
            Matrix4x4f m = sco.getAbsoluteMatrix();

            Matrix4x4f m2 = new Matrix4x4f();
      
            m2.initTrans(0.0f, 0.0f, -4.0f);  //(up-down,.../left-right/,.../back-forth)
            m.multRight(m2);
     
            m2.initZRot(-(float)Math.PI *0.0f);      
            m.multRight(m2);
      
            float fSeatRot = (float)(Math.PI * 2 / (float)numSeats);
            m2.initYRot(fSeatRot);
            m.multRight(m2);

            pov.setCameraMatrix(m);
        }
    }
}
Posted February 14, 2019· Edited February 14, 2019

Are the dummy restraints connected to the hidden restraints?

Because you could use bestdanis restraint script which takes a animation that you keyframe in blender and makes it move when the restraint moves

Well the monorail is attached to a hidden coaster train yes. Can I use two different scripts on the same object though? I thought you could only use one.

Posted February 14, 2019· Edited February 14, 2019

You can use multiple scripts per object, so you could use a seperate script to handle the animations if you wanted to do it that way. Looks like Train.getHarnessState() is the function you want. So presumably you'll keep checking the value then begin the animations when it switches.

Here's a script I made that might help with the keyframe animation. I made it for a rolling station, so It currently uses track triggers to start the animation


public class WaterboatRestraints extends Script implements TrackTriggerListener
{
        private Coaster c;
        private Train t;
        private SceneObject sco;

        private bool animation_enabled = false;
        private double animation_start;
        private double animation_end;
        private double animation_current = 2.0;

        private long time_current = 0;
        private long time_last;

        private TrackTrigger triggerOpen, triggerClose;


    public void onTrainEntering(TrackTrigger trigger, Train train)
    {
        if(train != t)
            return;

        if(trigger == triggerOpen && animation_current != 2.0)
        {
            animation_enabled = true;
            animation_start = 0.0;
            animation_end = 2.0;
            animation_current = 0.0;
        }
        else if (trigger == triggerClose && animation_current != 0.0)
        {
            animation_enabled = true;
            animation_start = 2.0;
            animation_end = 4.0;
            animation_current = 2.0;
        }
    }


    public void onTrainLeaving(TrackTrigger trigger, Train train)
    {
            return;
    }


    public void onNextFrame(float tick)
    {
        // Update timer count
        time_last = time_current;
        time_current = System.currentTimeMillis();
        double time_diff = (double)(time_current - time_last) / 1000;

        if (animation_enabled != true)
            return;

        // Play current animation
        animation_current += time_diff;
        sco.setKeyFrameAnimationTime(animation_current);

        if (animation_current >= animation_end)
        {
            animation_enabled = false;
            animation_current = animation_end;
        }
    }


    public bool onInit()
    {
        //load the attachment object
        sco = sim.getSceneObjectForEntityId(getParentEntityId());
        sco.setKeyFrameAnimationTime(animation_current); //start open
        c = sim.getCoasterAt(0);

        //Find the nearest Coaster
        TrackPos trackPos = sim.findNearestCoasterTrack(sco.getTranslation(), 5);
		if (trackPos == null)
		{
			System.err.println("WaterboatRestraints: No track found within 5 meters.");
			return false;
		}
		else
		{
			c = trackPos.getCoaster();
        }

        //Find the nearest train
        t = c.findNearestTrain(sco.getTranslation(), 5);

        // Add Trigger Listeners
        triggerOpen = c.getTrackTrigger("restraint_open");
        triggerClose = c.getTrackTrigger("restraint_close");
        triggerOpen.addTrackTriggerListener(this);
        triggerClose.addTrackTriggerListener(this);

        return true;
    }
}
Posted February 20, 2019· Edited February 20, 2019

Looking fantastic, as usual. I can't imagine you have too many "phases" left, unless you decide to start building out, now that the lake is fully encircled.

Also, looking at your YouTube channel, I had no idea that Aeris existed years ago as a standalone ride. Are there any plans to do the same for some of your other rides in Terra, particularly Atlas and Corsair? Those were some favorites of mine back in the day. I remember my mind going wild when I saw how they were meant to be part of a bigger park. Now, that can be reality.

Phase 4 will more than likely be the final phase of the park, as it will pretty much be finished. That doesn't mean to say that there isn't room for improvement or expansion, but that will come at the removal of older attractions. I can envisage an expansion of the lightning canyon area of the park eventually, but for now there are no plans beyond phase 4.

Terra actually started about 10 years ago when I designed and build Aeris in NL1. It was always designed as the centrepiece of a park, while rides like Atlas and Corsair were designed as part of a larger park, there was never the same planning that went into it as there was with Aeris. For example, I rooted through an old sketchbook to find these basic sketch designs I did when I was creating Aeris.

You may recognise a lot of what came to be built when I constructed Aeris, but there are also a number of differences. Originally, there was going to be a large oriental themed hotel (much like Ling Bao at Phantasialand) opposite the lake. This eventually turned into the Palace Hotel, which is now located next to the entrance.

Finally, here is a basic first shot of what the layout was originally intended to be. As you can see, a lot changed over the years. Desert became Lightning Canyon, the Monkey Jungle was replaced by Elysium, and moved to become the Wilds. Flooded ruins became the Badlands (the 'flooded' theme was then added to Elysium to become a futuristic Waterfall city). The Dark Forest and Mountain merged to become The Mountains. Pixie Wood moved and became Mystic Hollow, and finally the Oriental city was merged into Sky Kingdom.

Wow great fact. Maybe a nice idea to import it like some museum/walktrough inside the park.
There is a big empty place known as 'Cave In'. What did you want to do with that place?

Maybe it's an idea to make a sort of progress bar, where the fans can see how far you are or what is need to do, before the release? I can understand, that you can't be exactly, but just for an idea?

26 to 50 of 180