Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * This work uses content from the Sansar Knowledge Base. © 2017 Linden Research, Inc. Licensed under the Creative Commons Attribution 4.0 International License (license summary available at https://creativecommons.org/licenses/by/4.0/ and complete license terms available at https://creativecommons.org/licenses/by/4.0/legalcode).
- * This work uses content from LGG. © 2017 Greg Hendrickson. Licensed under the Creative Commons Attribution 4.0 International License (license summary available at https://creativecommons.org/licenses/by/4.0/ and complete license terms available at https://creativecommons.org/licenses/by/4.0/legalcode).
- */
- using Sansar.Script;
- using Sansar.Simulation;
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- namespace ufo
- {
- /// <summary>
- /// This is a script for turning a dynamic object into a movable vehicle
- /// As avatars enter the region, this script grabs their controls so that
- /// it can listen to see if they try to enter it when they are nearby
- /// Then it listens to the control inputs W A S D to apply linear velocity
- ///
- /// This script was written for a very alpha version of Sansar which does not have a way
- /// To sit on an object, or snap the avatar to it's position and rotation
- /// As such, it assumes that the avatar will be rotating inside the vehicle
- /// and makes use of this
- ///
- /// Because there is no sit, to keep the avatar in the vehicle, you must make the physical
- /// object help with this, build a min cage around the avatar to hold them in place || av ||
- /// This script does not support rotating the object to match the avatars facing direction
- /// It could, but as such this currently works best with objects that are radially symmetrical
- ///
- /// </summary>
- public class movingUFOVehicle : SceneObjectScript
- {
- #region different versions of the avatar driving
- /// <summary>
- /// This is used to save what object (avatar) is currently driving the vehicle. If it is empty, it will allow a new avatar to drive, etc
- /// </summary>
- private ObjectId objectInVehicle;
- /// <summary>
- /// the objectPrivate object of the driver so that we can get information off them easier
- /// </summary>
- private ObjectPrivate avObject;
- /// <summary>
- /// the agent private of the avatar driving
- /// </summary>
- private AgentPrivate avAgent;
- /// <summary>
- /// the animation component of the avatar driving, so that we can move the avatar or even get where their camera is facing
- /// </summary>
- AnimationComponent avAnim;
- #endregion
- #region we change these a lot in this script!
- /// <summary>
- /// This stores the current target speed, we increase this when they press WAD and decrease it over time to act like friction
- /// </summary>
- private float speed = 0.0f;
- /// <summary>
- /// Direction the avatar is facing on key press
- /// </summary>
- Sansar.Vector direction;
- /// <summary>
- /// Important direction that stores not only the direction, but also the speed we want the vehicle to go
- /// </summary>
- Sansar.Vector totalDirection;
- /// <summary>
- /// controls the engine sound settings, we increase the volume and the pitch with speed
- /// we put this globally so that we don't have to keep allocating memory to it each loop
- /// </summary>
- private PlaySettings Engine_Settings;
- #endregion
- #region constants
- /// <summary>
- /// Max speed we want to allow the object to go, anything higher than about 20 gets a little weird with physics
- /// </summary>
- private const float maxSpeed = 25.0f;
- /// <summary>
- /// When they press a button, how much target speed should we add
- /// </summary>
- private const float speedIncriment = 1.5f;
- /// <summary>
- /// Every updates per second, we decrease the speed by multiplying by this percent to emulate friction
- /// </summary>
- private const float decreaseSpeedPercentage = .95f;
- /// <summary>
- /// how many speed updates we should do per second, more will follow the av rotation better
- /// </summary>
- private const double updatesPerSecond = 10.0f;
- /// <summary>
- /// How many times per second we check for collisions, to play sounds or move out of the way
- /// </summary>
- private const double collisionChecksPerSecond = 5;
- /// <summary>
- /// This controls how strongly we will change our direction based on the way the user is facing
- /// We don't want it one, else it will look like the object can turn on a dime, add some slide to make
- /// it feel like it has momentum
- /// </summary>
- private const float turnPowerPercent = .4f;
- /// <summary>
- /// What channel the script will talk to other vehicles and a helper script to reset their position if things go crazy
- /// </summary>
- private const int channel = 3;
- #endregion
- #region mostly stays the same the whole time
- /// <summary>
- /// This array holds the list of keys to listen to for entering and exiting the vehicle
- /// </summary>
- private string[] keysToUseEnterandExit = { "Key_1", "Key_2", "Key_3", "Key_4", "Key_F5", "Key_F6", "Key_F7", "Key_F8", "Key_F9", "Key_F10", "Key_F11", "Key_F12" };
- /// <summary>
- /// A few seconds after initialization (after the vehicle settles on the ground) save it's position as the start position to return to if stuff goes crazy
- /// </summary>
- private Sansar.Vector startPos;
- /// <summary>
- /// So this is the sound component that is attached to the object
- /// You will need to make this!!!
- /// on the worlds object viewer, right click your vehicle, and add a audio component.
- /// Don't bother giving it a sound or volume, but you can play with the shape
- /// We use this so that the sounds move with this object instead of staying still at a location
- /// </summary>
- private AudioComponent LocalAudioComponent = null;
- /// <summary>
- /// At start, we grab our objects rigid body so that we can move it and see where it is in the script
- /// </summary>
- private RigidBodyComponent RigidBody = null;
- /// <summary>
- /// The sound of the engine, make it around 1 second long so the pitch shifts sort of blend in together
- /// This must be public so that you can assign a sound to this script
- /// </summary>
- public SoundResource Engine_Sound;
- /// <summary>
- /// Sound to play on collision, must be public so that it can be assigned
- /// </summary>
- public SoundResource Colision_Sound;
- /// <summary>
- /// Whether or not to complain to the log
- /// </summary>
- public Boolean debug = false;
- /// <summary>
- /// Just a variable to store the random generator
- /// </summary>
- private Random Rnd = new Random();
- #endregion
- /// <summary>
- /// Runs when the script starts, initialize vars, start coroutines, etc
- /// </summary>
- public override void Init()
- {
- // When unhandled exceptions occur, they may be caught with this event handler.
- // Certain exceptions may not be recoverable at all and may cause the script to
- // immediately be removed. This will also break out of any loops in coroutines!
- Script.UnhandledException += UnhandledException;
- //Reset our vars to defaults
- objectInVehicle = ObjectId.Invalid;
- direction = Sansar.Vector.Zero;
- totalDirection = Sansar.Vector.Zero;
- Engine_Settings = PlaySettings.PlayOnce;
- CollisionEventType trackedEvents = 0;
- trackedEvents |= CollisionEventType.cCharacterContact;
- trackedEvents |= CollisionEventType.cRigidBodyContact;
- if (RigidBody == null)
- {
- if (!ObjectPrivate.TryGetFirstComponent(out RigidBody))
- {
- // This example will only work on a dynamic object with a rigid body. Bail if there isn't one.
- return;
- }
- }
- if (LocalAudioComponent == null)
- {
- if (!ObjectPrivate.TryGetFirstComponent(out LocalAudioComponent))
- {
- return;
- }
- }
- startPos = RigidBody.GetPosition();
- //Start up the coroutines
- StartCoroutine(checkIfAvatarIsStillWithUs);
- StartCoroutine(moveUFO);// Convert the supplied booleans to the correct CollisionEventType to track
- StartCoroutine(CheckForCollisions, trackedEvents);
- //Grab the new user event to grab hot keys
- ScenePrivate.User.Subscribe(User.AddUser, NewUser);
- //Grab the chat event for listening to each other vehicle and resetting
- ScenePrivate.Chat.Subscribe(channel, "script", OnChat);
- //in 3 seconds, after we fall to and settle on the ground, save the start position
- Timer.Create(TimeSpan.FromSeconds(3), () => saveStartPos());
- }
- #region coroutines!
- /// <summary>
- /// Subroutine to check the av position to see if they fell out, and if so, reset
- /// </summary>
- void checkIfAvatarIsStillWithUs()
- {
- while (true)
- {
- if (weHaveADriver())
- {
- //See if they are too far away, if yes, they fell out, release!
- if (GetDistance(avObject.Position, RigidBody.GetPosition()) > 4)
- {
- resetEverythingish();
- if (debug) Log.Write(string.Format("We physically lost controlled by {0} , {1} , {2}", avAgent.AgentInfo.Name, objectInVehicle, avObject));
- }
- }
- Wait(TimeSpan.FromSeconds(5));
- }
- }
- /// <summary>
- /// Subroutine to apply friction and enforce speed, engine sound, etc
- /// </summary>
- void moveUFO()
- {
- while (true)
- {
- if (weHaveADriver())
- {
- try
- {
- // This is used for volume stuff, basically how fast are we going as a percent? 0.00 to 1.00
- float magPercent = (((float)GetMagnitude(totalDirection)) / maxSpeed);
- // enforce the speed and apply it
- WaitFor(RigidBody.SetLinearVelocity,totalDirection);
- // apply a fake sort of friction, we will use this next loop
- totalDirection *= decreaseSpeedPercentage;
- //make the loudness be louder the faster, and make the pitch go up as well, then play it
- Engine_Settings.Loudness = (magPercent * 50.0f) - 50.0f;
- if (magPercent > .05) Engine_Settings.Loudness += 10.0f;
- Engine_Settings.PitchShift = ((magPercent / maxSpeed) * 25.0f);
- if (Engine_Sound != null && Engine_Settings.Loudness > -50.0f) LocalAudioComponent.PlaySoundOnComponent(Engine_Sound, Engine_Settings);//.OnFinished(OnSoundFinished);
- }
- catch (Exception)
- { // If we got a bad force value it could cause an exception in SetLinearVelocity.
- continue;
- }
- }
- Wait(TimeSpan.FromSeconds(1.0 / updatesPerSecond));
- }
- }
- /// <summary>
- /// Check to see if we hit someone, play a sound if we did, subroutine
- /// </summary>
- /// <param name="trackedEvents"></param>
- private void CheckForCollisions(CollisionEventType trackedEvents)
- {
- while (true)
- {
- // This will block the coroutine until a collision happens
- CollisionData data = (CollisionData)WaitFor(RigidBody.Subscribe, trackedEvents, Sansar.Script.ComponentId.Invalid);
- // Check that the collision is good.
- if (
- (data.HitObject != null)
- // && (objectInVehicle != ObjectId.Invalid)
- && (data.HitObject.ObjectId != objectInVehicle)
- && (GetMagnitude(data.HitObject.Position) > 1)
- )
- {
- if (data.HitObject == null)
- {
- // This is slightly more common, collided with something were no object was given from the physics system.
- // Again, just sleep a little and continue.
- Log.Write("Hit nothing? " + data.HitComponentId);
- //Wait(TimeSpan.FromSeconds(0.2));
- continue;
- }
- // This position - collision object position gives a vector away from the object we collided with.
- // This is not "away from the collision point". Complex shapes or large objects will confuse this simple math.
- Sansar.Vector direction = ObjectPrivate.Position - data.HitObject.Position;
- direction = direction.Normalized();
- if (Math.Abs(direction.X) < 0.1 && Math.Abs(direction.Y) < 0.1)
- {
- // This object is mostly above or below us: it is probably the floor. ignore
- continue;
- }
- try
- {
- if (Colision_Sound != null) LocalAudioComponent.PlaySoundOnComponent(Colision_Sound, PlaySettings.PlayOnce);
- //We could try to propel away RigidBody.AddLinearImpulse(direction * 100000);
- }
- catch (Exception e)
- {
- //if(debug)Log.Write("Collision Exception " + e.GetType().ToString() + " in AddLinearImpulse for value: " + (direction * 100000).ToString());
- }
- }
- // Always wait after a collision before checking for more collisions to reduce duplicates
- Wait(TimeSpan.FromSeconds(1.0 / collisionChecksPerSecond));
- }
- }
- #endregion
- #region chat handling
- /// <summary>
- /// We use the chat to talk to other vehicles to find out their position, and reset ourself if it looks like there is a physics problem
- /// </summary>
- /// <param name="Channel"></param>
- /// <param name="Source"></param>
- /// <param name="SourceId"></param>
- /// <param name="SourceScriptId"></param>
- /// <param name="Message"></param>
- void OnChat(int Channel, string Source, SessionId SourceId, ScriptId SourceScriptId, string Message)
- {
- if (debug) Log.Write(string.Format("vehicle of script {0} got chat Channel {1},Source {2},ScriptId {3},Message {4}", this.Script.ID, Channel, Source, SourceScriptId, Message));
- // ignore any messages that are not from a script
- if (SourceScriptId != ScriptId.Invalid)
- {
- object talkingScript = ScenePrivate.FindScript(SourceScriptId);
- if (talkingScript == null)
- {
- Log.Write(LogLevel.Warning, "Unable to find the script who was talking.");
- return;
- }
- if (Message.StartsWith("UFOs where are you?"))
- {
- messageAllScripts();
- }
- //another vehicle has told us where they are, check to see if we are too close, and if we don't have a driver, go back to the start
- if (Message.StartsWith("I am a vehicle at position id "))
- {
- //don't go home if someone is driving!
- if (!weHaveADriver())
- {
- Sansar.Vector otherUFOPos = vectorFromString(Message.Substring(26));
- Sansar.Vector myPos2 = RigidBody.GetPosition();
- double dist = GetDistance(otherUFOPos, myPos2);
- if (debug) Log.Write(string.Format("Got a vehicle at position {0} i am at position {1} and distance of {2} from {3} and i am {4}", otherUFOPos, myPos2, dist, SourceScriptId, Script.ID));
- if (((dist < 4.0 && (SourceScriptId != Script.ID)) || Math.Abs(myPos2.Z - startPos.Z) > 1.0))
- {
- resetEverythingish(true);
- }
- }
- //notes.Add(SourceScriptId);
- //logCurrentDictionary();
- }
- }
- }
- /// <summary>
- /// Uses ScriptId.AllScript to message all other listening vehicles
- /// </summary>
- private void messageAllScripts()
- {
- ScenePrivate.Chat.MessageScript("I am a vehicle at position id " + RigidBody.GetPosition().ToString(), ScriptId.AllScripts, channel);
- }
- #endregion
- #region grabbing the hot keys
- /// <summary>
- /// When a new user enters, grab their keys to see if they try to control this vehicle
- /// </summary>
- /// <param name="action"></param>
- /// <param name="userSessionId"></param>
- /// <param name="data"></param>
- void NewUser(string action, SessionId userSessionId, string data)
- {
- // Set up the hot key listener.
- SubscribeToHotkeys(userSessionId);
- //if(one_to_six_not_7_to_12) Timer.Create(TimeSpan.FromSeconds(30), () => { messageUser(userSessionId); });
- }
- /// <summary>
- /// Ties the avatars keys to the functions to enter or move our vehicle
- /// </summary>
- /// <param name="userId"></param>
- private void SubscribeToHotkeys(SessionId userId)
- {
- AgentPrivate agent = ScenePrivate.FindAgent(userId);
- // Lookup the scene object for this agent
- ObjectPrivate agentObejct = ScenePrivate.FindObject(agent.AgentInfo.ObjectId);
- if (debug) Log.Write(string.Format("User id is {0} and obj id is {1}", userId, agent.AgentInfo.ObjectId));
- if (agentObejct == null) { Log.Write($"Unable to find a SceneObject component for user {userId}"); return; }
- // Lookup the animation component. There should be just one, so grab the first, we need this to tie to the keys
- AnimationComponent animationComponent = null;
- if (!agentObejct.TryGetFirstComponent(out animationComponent)) { Log.Write($"Unable to find an animation component on user {userId}"); return; }
- // Listen for all the list of number key presses to enter the vehicle
- int keyNum = 0; foreach (string key in keysToUseEnterandExit)
- {
- int localKeyNum = keyNum;
- //now I know how to use lambda statements.. sometimes
- animationComponent.Subscribe(key.Replace("Key_F", "Key_"), (BehaviorName, ComponentId) => HopInVehicle(localKeyNum, ComponentId), true);
- keyNum++;
- }
- //Listen to WASD to move the vehicle
- animationComponent.Subscribe("Key_W", (BehaviorName, ComponentId) => goForeward(1, ComponentId), true);
- animationComponent.Subscribe("Key_A", (BehaviorName, ComponentId) => goForeward(1, ComponentId), true);
- animationComponent.Subscribe("Key_D", (BehaviorName, ComponentId) => goForeward(1, ComponentId), true);
- animationComponent.Subscribe("Key_S", (BehaviorName, ComponentId) => slowDown(1, ComponentId), true);
- }
- #endregion
- #region mostly small helper methods
- /// <summary>
- /// The user has left, or we are resetting
- /// Resets the locations and rotations and speed etc
- /// </summary>
- /// <param name="resetStartPos"></param>
- void resetEverythingish(bool resetStartPos = false)
- {
- objectInVehicle = ObjectId.Invalid;
- speed = 0;
- totalDirection = Sansar.Vector.Zero;
- RigidBody.SetLinearVelocity(Sansar.Vector.Zero);
- RigidBody.SetAngularVelocity(Sansar.Vector.Zero);
- RigidBody.SetOrientation(Sansar.Vector.Up);
- if (resetStartPos) RigidBody.SetPosition(startPos);
- }
- /// <summary>
- /// We use this mostly to catch error so the whole script does not crash
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void UnhandledException(object sender, Exception e)
- {
- // Depending on the script scheduling policy, the exception may or may not be recoverable.
- // An unrecoverable exception that can be handled will only be given a short time to run
- // so the handler method needs to be kept small.
- // Any exception thrown from this method will terminate the script regardless of the value
- // of UnhandledExceptionRecoverable
- if (!Script.UnhandledExceptionRecoverable)
- {
- Log.Write("Unrecoverable exception, this is useless.");
- Log.Write(e.ToString());
- }
- else
- {
- Log.Write("Exception!");
- Log.Write(e.ToString());
- // While we have recovered, whatever coroutine had the exception will have stopped.
- // The script will still run so logs can be recovered.
- }
- }
- /// <summary>
- /// just saves the start position, used to have more here
- /// </summary>
- public void saveStartPos()
- {
- startPos = RigidBody.GetPosition();
- }
- /// <summary>
- /// Whether or not we have a driver currently
- /// </summary>
- /// <returns></returns>
- private Boolean weHaveADriver()
- {
- return objectInVehicle != ObjectId.Invalid;
- }
- #endregion
- #region responseTOKeypresses
- /// <summary>
- /// The avatar is pressing a movement key that would increase speed
- /// Don't care about what direction, as we are going to use the avatars fwd vector instead, let the experience handle the avatar
- /// </summary>
- /// <param name="nothing"></param>
- /// <param name="ComponentId"></param>
- private void goForeward(int nothing, ComponentId ComponentId)
- {
- //check to see if our driver pressed this button
- if (objectInVehicle == ComponentId.ObjectId)
- {
- float currentSpeed = (float)GetMagnitude(totalDirection);
- //get the direction the avatar is now facing, we will try to go that direction
- direction = avObject.ForwardVector;
- //This could be weird, don't go up or down
- direction.Z = 0;
- //We are not even moving fast, so lets force in a slight boost so we are not working with 0s when we normalize
- if (currentSpeed <= speedIncriment)
- {
- totalDirection = direction;
- }
- //So i broke apart the math some, but here is the idea
- //Find the direction we are currently moving, add in a percent of the direction the avatar is facing to get a direction between the two, then go that way
- Sansar.Vector origonTotalDirection = totalDirection;
- Sansar.Vector totalNormalized = totalDirection.Normalized();
- Sansar.Vector newDirection = totalNormalized + (direction.Normalized() * turnPowerPercent);
- Sansar.Vector newDirectionNormalize = newDirection.Normalized();
- //totalDirection = (totalDirection.Normalized() + (direction.Normalized() * turnPowerPercent)).Normalized() * (currentSpeed + speedIncriment);
- totalDirection = newDirectionNormalize * (currentSpeed + speedIncriment);
- if (debug) Log.Write(string.Format("TD was {0} and normalized is {1} and direction av is {2} and with turn %{3} new direction is {4} normal is {5} so final is {6}",
- origonTotalDirection, totalNormalized, direction, turnPowerPercent, newDirection, newDirectionNormalize, totalDirection));
- if (GetMagnitude(totalDirection) > maxSpeed) totalDirection = totalDirection.Normalized() * maxSpeed;
- //This is just a helper because sometimes stuff is crazy
- RigidBody.SetOrientation(Sansar.Vector.Up);
- }
- }
- /// <summary>
- /// Avatar presses S, lower the speed , but otherwise use goFwd
- /// </summary>
- /// <param name="nothing"></param>
- /// <param name="ComponentId"></param>
- private void slowDown(int nothing, ComponentId ComponentId)
- {
- //check to see if our driver pressed this button
- if (objectInVehicle == ComponentId.ObjectId)
- {
- speed = (float)GetMagnitude(totalDirection) - (speedIncriment * 2);
- goForeward(nothing, ComponentId);
- }
- }
- /// <summary>
- /// This happens when a user preses a key that would enter them into a vehicle
- /// Check to see if they are close by and facing the vehicle, then assign them to us so that we care about their controls
- /// also, throw them into the cockpit
- /// </summary>
- /// <param name="soundNum"></param>
- /// <param name="ComponentId"></param>
- private void HopInVehicle(int soundNum, ComponentId ComponentId)
- {
- if (!weHaveADriver())
- {
- ObjectPrivate agentObejct = ScenePrivate.FindObject(ComponentId.ObjectId);
- Sansar.Vector targetPos = RigidBody.GetPosition();
- //We check to see if they are looking at us, project a vector form their position 2m fwd, and see if it is close to us
- if (GetDistance(agentObejct.Position + (agentObejct.ForwardVector * 2), targetPos) < 4)
- {
- resetEverythingish();
- //This is what makes us believe we have a driver now
- objectInVehicle = ComponentId.ObjectId;
- //Grab the other objects of this avatar for later use
- avAgent = ScenePrivate.FindAgent(ComponentId.ObjectId);
- avObject = agentObejct;
- avAnim = null;
- if (debug) Log.Write(string.Format("We are now controlled by {0} , {1} , {2}", avAgent.AgentInfo.Name, objectInVehicle, avObject));
- //Throw the avatar above us into the cockpit (centered in this case)
- if (avObject.TryGetFirstComponent(out avAnim))
- {
- targetPos.Z += 1.0f;
- avAnim.SetPosition(targetPos);
- }
- }
- }
- //We already have a driver, and further more he pressed a button to leave
- else if (objectInVehicle == ComponentId.ObjectId)
- {
- //hop out
- avAnim.SetPosition(RigidBody.GetPosition() - (avObject.ForwardVector * 5));
- if (debug) Log.Write(string.Format("We are no longer controlled by {0} , {1} , {2}", avAgent.AgentInfo.Name, objectInVehicle, avObject));
- resetEverythingish();
- }
- }
- #endregion
- #region mostly library helper functions
- /// <summary>
- /// Get the magnitude of a vector
- /// </summary>
- /// <param name="vector"></param>
- /// <returns></returns>
- private double GetMagnitude(Sansar.Vector vector)
- {
- return Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z);
- }
- /// <summary>
- /// Get the distance between 2 vectors
- /// </summary>
- /// <param name="vector1"></param>
- /// <param name="vector2"></param>
- /// <returns></returns>
- private double GetDistance(Sansar.Vector vector1, Sansar.Vector vector2)
- {
- return Math.Sqrt(Math.Pow((vector1.X - vector2.X), 2) + Math.Pow((vector1.Y - vector2.Y), 2) + Math.Pow((vector1.Z - vector2.Z), 2));
- }
- /// <summary>
- /// Get a random vector
- /// </summary>
- /// <param name="magnitude"></param>
- /// <param name="weights">a vector to multiple against to restrict the randomness to certain axis</param>
- /// <returns></returns>
- Sansar.Vector randomVector(double magnitude, Sansar.Vector weights)
- {
- if (GetMagnitude(weights) == 0)
- {
- weights = new Sansar.Vector(1, 1, 1);
- }
- return new Sansar.Vector(
- (float)(Rnd.NextDouble() * magnitude - (magnitude / 2.0)) * weights.X,
- (float)(Rnd.NextDouble() * magnitude - (magnitude / 2.0)) * weights.Y,
- (float)(Rnd.NextDouble() * magnitude - (magnitude / 2.0)) * weights.Z
- );
- }
- /// <summary>
- /// Takes a string from vector.tostring and turns it back into a vector
- /// Note, this is sloppy and could crash if the string is bad, use try parse, etc
- /// </summary>
- /// <param name="input"></param>
- /// <returns></returns>
- Sansar.Vector vectorFromString(string input)
- {
- Sansar.Vector output = new Sansar.Vector(0, 0, 0);
- string[] splitted = input.Replace("<", "").Replace(">", "").Replace(" ", "").Split(new char[] { ',' });
- if (splitted.Length == 3)
- {
- output.X = float.Parse(splitted[0]);
- output.Y = float.Parse(splitted[1]);
- output.Z = float.Parse(splitted[2]);
- }
- return output;
- }
- #endregion
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement