// Override Init to set up event handlers and start coroutines. 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; // If the object had its components set in the editor they should now have the member initialized // [Editor support for Component types is not yet implemented] // The component can also be found dynamically if (RigidBody == null) { if (!ObjectPrivate.TryGetFirstComponent(out RigidBody)) { // Since object scripts are initialized when the scene loads, no one will actually see this message. Log.Write("There is no RigidBodyComponent attached to this object."); return; } } // Initialize the Home position Home = RigidBody.GetPosition(); // StartCoroutine will queue CheckForCollisions to run // Arguments after the coroutine method are passed as arguments to the method. StartCoroutine(CheckForCollisions); // Clamp our chaos to [0,1] StartCoroutine(Movement, Speed, Chaos.Clamp(0.0f, 1.0f)); }
// This is a lot of complex math on choosing some "random" movement, keeping it near home and handling error cases. private void Movement(float speed, float chaos) { // Initialize a random number generator. Random rng = new Random(); // Pick a direction to start with - mostly in the X/Y plane, but just a little push up. Direction = new Sansar.Vector((float)(0.5 - rng.NextDouble()), (float)(0.5 - rng.NextDouble()), 0.02f, 0.0f); // This will just continually try to move the object (the Wait at the bottom is important!) while (true) { // Calculate how far we are from our home point. float distance = (RigidBody.GetPosition() - Home).Length(); // Pick a new direction based on Chaos level, or if we have wandered off too far if (rng.NextDouble() <= Chaos || distance > Range) { // If we are far from home point us at home before adjusting the position. if (distance > Range) { // Move toward the center. Direction = (Home - RigidBody.GetPosition()).Normalized(); // Note: still letting the randomize adjust the heading. } // This is the most bogus direction adjusting logic you will see today. Direction.X = Direction.X + (float)(0.5 - rng.NextDouble()); Direction.Y = Direction.Y + (float)(0.5 - rng.NextDouble()); Direction.Z = 0.02f; Direction = Direction.Normalized(); } // AddLinearImpulse can be picky on accepted values, especially if any math above breaks or the speed is set too high. // It will throw an exception if it doesn't like it. This will just skip try { Log.Write("PUSH! " + Direction.ToString() + " * " + speed + " => " + (Direction * speed).ToString()); RigidBody.AddLinearImpulse(Direction * speed); } catch (Exception e) { Log.Write("Exception " + e.GetType().ToString() + " in AddLinearImpulse for value: " + (Direction * speed).ToString()); // That direction was bad, so lets choose a new one. Direction.X = (float)(0.5 - rng.NextDouble()); Direction.Y = (float)(0.5 - rng.NextDouble()); Direction.Z = 0.02f; Direction = Direction.Normalized(); } // Wait after each push for between 0.5 and 1.5 seconds. Wait(TimeSpan.FromSeconds(0.5 + rng.NextDouble())); } }
// Putting the collision event into a coroutine helps to reduce duplicate events // After every collision we give a small bump then sleep this coroutine before waiting on another collision. // An Action event handler would get a large queue of events for colliding with the same object which is harder to deal with. private void CheckForCollisions() { while (true) { // This will block the coroutine until a collision happens CollisionData data = (CollisionData)WaitFor(RigidBody.Subscribe, CollisionEventType.AllCollisions, Sansar.Script.ComponentId.Invalid); 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. // This is overly simplistic and will fail for large objects or sloped floors. // Sleep a little and continue. Wait(TimeSpan.FromSeconds(0.2)); continue; } // direction is now pointing _away_ from what we collided with, set it as our Direction for Movement. Direction = direction; try { // Apply an immediate bump away from what we collided with. Log.Write("Bump! " + Direction.ToString() + " * " + Speed + " => " + (Direction * Speed).ToString()); RigidBody.AddLinearImpulse(Direction * Speed); } catch (Exception e) { Log.Write("Collision Exception " + e.GetType().ToString() + " in AddLinearImpulse for value: " + (Direction * Speed).ToString()); } // Wait before checking for more collisions to avoid duplicate collisions and give a chance to separate from the other object. Wait(TimeSpan.FromSeconds(0.2)); } }
public override void Init() { if (RigidBody == null) { if (!ObjectPrivate.TryGetFirstComponent(out RigidBody)) { return; } } lightPositionOffset = new Sansar.Vector(0, 0, 2); startRot = RigidBody.GetOrientation(); //we want this to float RigidBody.SetMass(0); //the default light when you add it is off by -90 degrees lightOnObjectDirection = Quaternion.FromEulerAngles(new Sansar.Vector(0, 0, (float)(Math.PI / -2.0), 0)); StartCoroutine(UpdateLoop); }
private void SetRandomColorAndIntensityOfAllLights() { foreach (var light in lights) { // Pick a random color but don't let it be too dark or else the relative intensity doesn't work well Sansar.Vector randomVector = new Sansar.Vector((float)rnd.NextDouble(), (float)rnd.NextDouble(), (float)rnd.NextDouble()); if (randomVector.LengthSquared() < 0.5f) { randomVector = randomVector.Normalized(); } Sansar.Color randomColor = new Sansar.Color(randomVector.X, randomVector.Y, randomVector.Z); // Pick a random intensity from min to max float randomIntensity = randomMinIntensity + (randomMaxIntensity - randomMinIntensity) * (float)rnd.NextDouble(); light.SetColorAndIntensity(randomColor, randomIntensity); } }
private void UpdateLoop() { while (true) { //this is only one object, so we have it hard coded in to only follow the first agent., you could split this up later Boolean got1 = false; foreach (AgentPrivate agent in ScenePrivate.GetAgents()) { if (got1 == false) { ObjectPrivate agentObejct = ScenePrivate.FindObject(agent.AgentInfo.ObjectId); AnimationComponent anim; if (agentObejct.TryGetFirstComponent(out anim)) { Sansar.Vector fwd = anim.GetVectorAnimationVariable("LLCameraForward"); //Builds a rotation from the fwd vector Quaternion newRot = Quaternion.FromLook(fwd, Sansar.Vector.Up); //This is a basic check to make sure the camera rotation isnt all 0s if (fwd.LengthSquared() > 0) { try { RigidBody.SetAngularVelocity(Vector.Zero); RigidBody.SetLinearVelocity(Vector.Zero); RigidBody.SetPosition(agentObejct.Position + lightPositionOffset); //Order of multiplication matters here I think, start with the base rotation, //multiply by and base offset rotation for the light, then multiply by the rotation of the fwd //Keep in mind that multiplying quad A by quad b will rotate quad A by quad b RigidBody.SetOrientation(QuaternionToVector(startRot * lightOnObjectDirection * newRot).Normalized()); } catch { } } } got1 = true; } } Wait(TimeSpan.FromSeconds(.05)); } }
public void OnFire(AnimationComponent animationComponent) { if (RezThrottled == true) { // TODO: Play a "click" / misfire sound. return; } ObjectPrivate characterObject = ScenePrivate.FindObject(animationComponent.ComponentId.ObjectId); if (Fire_Sound != null) { ScenePrivate.PlaySoundAtPosition(Fire_Sound, characterObject.Position, SoundSettings); } Sansar.Vector cameraForward = animationComponent.GetVectorAnimationVariable("LLCameraForward"); cameraForward.W = 0.0f; cameraForward = cameraForward.Normalized(); Sansar.Vector offset = new Sansar.Vector(cameraForward[0], cameraForward[1], 0, 0).Normalized(); Sansar.Vector new_pos = new Sansar.Vector(0f, 0f, 1.5f, 0f); new_pos += characterObject.Position; // This script is on the Avatar, so the Avatar is the owning object. new_pos += (offset * 0.6f); // Add to the world just in front of the avatar. float speed = InitialSpeed; if (InitialSpeed < 1.0f || InitialSpeed > 200.0f) { const float defaultSpeed = 10.0f; Log.Write(string.Format("Bad CannonBall Speed: {0}, using default {1}", InitialSpeed, defaultSpeed)); speed = defaultSpeed; } Sansar.Vector vel = cameraForward * speed; StartCoroutine(RezCannonball, new_pos, Quaternion.FromLook(cameraForward, Vector.Up), vel); Timer.Create(TimeSpan.FromSeconds(Delay), () => { animationComponent.Subscribe(ListenEvent, (data) => { OnFire(animationComponent); }, false); }); }
private void Subscribe(ScriptEventData sed) { if (subscriptions == null) { subscriptions = SubscribeToAll(TurnOnEvent, (data) => { if (TurnOnFadeTime > 0.0f) { previousColor = lights[0].GetNormalizedColor(); previousIntensity = lights[0].GetRelativeIntensity(); targetColor = initialColor; targetIntensity = initialIntensity; interpolationDuration = TurnOnFadeTime; interpolationTime = TurnOnFadeTime; interpolationActive = true; } else { interpolationActive = false; SetColorAndIntensityOfAllLights(initialColor, initialIntensity); } }); subscriptions += SubscribeToAll(TurnOffEvent, (data) => { if (TurnOffFadeTime > 0.0f) { previousColor = lights[0].GetNormalizedColor(); previousIntensity = lights[0].GetRelativeIntensity(); targetColor = Sansar.Color.Black; targetIntensity = 0.0f; interpolationDuration = TurnOffFadeTime; interpolationTime = TurnOffFadeTime; interpolationActive = true; } else { interpolationActive = false; SetColorAndIntensityOfAllLights(Sansar.Color.Black, 0.0f); } }); subscriptions += SubscribeToAll(TurnRandomEvent, (data) => { if (TurnRandomFadeTime > 0.0f) { // Pick a random color but don't let it be too dark or else the relative intensity doesn't work well Sansar.Vector randomVector = new Sansar.Vector((float)rnd.NextDouble(), (float)rnd.NextDouble(), (float)rnd.NextDouble()); while (randomVector.LengthSquared() < 0.1f) { randomVector = new Sansar.Vector((float)rnd.NextDouble(), (float)rnd.NextDouble(), (float)rnd.NextDouble()); } if (randomVector.LengthSquared() < 0.5f) { randomVector = randomVector.Normalized(); } Sansar.Color randomColor = new Sansar.Color(randomVector.X, randomVector.Y, randomVector.Z); // Pick a random intensity from min to max float randomIntensity = randomMinIntensity + (randomMaxIntensity - randomMinIntensity) * (float)rnd.NextDouble(); previousColor = lights[0].GetNormalizedColor(); previousIntensity = lights[0].GetRelativeIntensity(); targetColor = randomColor; targetIntensity = randomIntensity; interpolationDuration = TurnRandomFadeTime; interpolationTime = TurnRandomFadeTime; interpolationActive = true; } else { interpolationActive = false; SetRandomColorAndIntensityOfAllLights(); } }); } if (HasFadeTime()) { StartInterpolation(); } }
public void RezCannonball(Sansar.Vector initial_pos, Sansar.Quaternion rotation, Sansar.Vector vel) { ScenePrivate.CreateClusterData createData = null; try { try { if (Projectile != null) { createData = (ScenePrivate.CreateClusterData)WaitFor(ScenePrivate.CreateCluster, Projectile, initial_pos, rotation, vel); } else { // No projectile override - just use cannon balls. createData = (ScenePrivate.CreateClusterData)WaitFor(ScenePrivate.CreateCluster, "Export/CannonBall/", initial_pos, rotation, vel); } } catch (ThrottleException e) { Log.Write($"Too many rezzes! Throttle rate is {e.MaxEvents} per {e.Interval.TotalSeconds}."); RezThrottled = true; Wait(e.Interval - e.Elapsed); // Wait out the rest of the interval before trying again. RezThrottled = false; return; } if (createData.Success) { ObjectPrivate obj = createData.ClusterReference.GetObjectPrivate(0); if (obj != null) { RigidBodyComponent RigidBody = null; if (obj.TryGetFirstComponent(out RigidBody)) { //exit after 3 hits or 1 character hit for (int i = 0; i < 3; i++) { CollisionData data = (CollisionData)WaitFor(RigidBody.Subscribe, CollisionEventType.AllCollisions, ComponentId.Invalid); if ((data.EventType & CollisionEventType.CharacterContact) != 0) { // Use the scene to find the full object API of the other object. AgentPrivate hit = ScenePrivate.FindAgent(data.HitComponentId.ObjectId); if (hit != null) { ObjectPrivate otherObject = ScenePrivate.FindObject(data.HitComponentId.ObjectId); if (otherObject != null) { if (Hit_Sound != null) { ScenePrivate.PlaySoundAtPosition(Hit_Sound, otherObject.Position, SoundSettings); } if (Teleport_On_Hit) { AnimationComponent anim; if (otherObject.TryGetFirstComponent(out anim)) { anim.SetPosition(SpawnPoints[rng.Next(SpawnPoints.Count)]); } } } break; } } } } } } } catch (Exception e) { Log.Write(e.ToString()); } finally { if (createData.ClusterReference != null) { try { createData.ClusterReference.Destroy(); } catch (Exception) { } } } }