/// <summary>Samples a new orbiter of this type.</summary> /// <param name="manager">The manager.</param> /// <param name="center">The center.</param> /// <param name="dominantAngle">The dominant angle.</param> /// <param name="random">The random.</param> /// <returns></returns> internal void Sample(IManager manager, int center, float dominantAngle, IUniformRandom random) { if (random != null && ChanceToExist <= random.NextDouble()) { return; } var radius = UnitConversion.ToSimulationUnits(SampleOrbitRadius(random)); var eccentricity = SampleEccentricity(random); var angleOffset = SampleAngleOffset(random); var travelSpeed = UnitConversion.ToSimulationUnits(SampleTravelSpeed(random)); var periodOffset = random == null ? 0f : (float)random.NextDouble(); // Compute minor and major radius. float a, b; ComputeRadii(radius, eccentricity, out a, out b); // Get period. Figure out circumference using Ramanujan's approximation. var circumference = MathHelper.Pi * (3 * (a + b) - (float)Math.Sqrt((3 * a + b) * (a + 3 * b))); var period = circumference / travelSpeed * Settings.TicksPerSecond; var entity = FactoryLibrary.SamplePlanet(manager, Name, random); // Make it move around its center. manager.AddComponent <EllipsePath>(entity) .Initialize(center, a, b, dominantAngle + angleOffset, period, MathHelper.TwoPi * periodOffset); // Recurse. if (Moons != null) { Moons.Sample(manager, entity, random); } }
// TODO in case we need this somewhere else it might be a good idea to move this to the EntityFactory private void CreateAsteroid(FarPosition position, IUniformRandom random, ContentManager content) { // Randomly scale and rotate it. var scale = (float)random.NextDouble(0.5f, 1f); var angle = (float)random.NextDouble() * MathHelper.TwoPi; // Determine shape for physics system. var textureName = "Textures/Asteroids/rock_" + random.NextInt32(1, 14); var texture = content.Load <Texture2D>(textureName); var hull = new List <Vector2>(TextureConverter.DetectVertices(texture, 8f, textureName: textureName)[0]); for (var k = 0; k < hull.Count; ++k) { hull[k] -= new Vector2(texture.Width / 2f, texture.Height / 2f); hull[k] = XnaUnitConversion.ToSimulationUnits(hull[k]) * scale; } var polygons = EarClipDecomposer.ConvexPartition(hull); // Create physical representation. var entity = Manager.AddEntity(); var body = Manager.AddBody(entity, position, angle, Body.BodyType.Dynamic); foreach (var polygon in polygons) { Manager.AttachPolygon(body, polygon, density: 1000f, restitution: 0.2f); } // Slow down to allow reaching sleep state again. body.LinearDamping = 0.05f * Space.Util.Settings.TicksPerSecond; body.AngularDamping = 0.025f * Space.Util.Settings.TicksPerSecond; // Bounds of the asteroid for rendering culling. We use the diagonal for a loose fit that // contains every possible rotated state of the texture. var width = UnitConversion.ToSimulationUnits(texture.Width); var height = UnitConversion.ToSimulationUnits(texture.Height); var diagonal = (float)Math.Sqrt(width * width + height * height); var bounds = new FarRectangle(-diagonal / 2, -diagonal / 2, diagonal, diagonal); // Rendering stuff. Manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); Manager.AddComponent <Indexable>(entity).Initialize(bounds, InterpolationSystem.IndexId); Manager.AddComponent <SimpleTextureDrawable>(entity).Initialize(textureName, scale); // Auto removal. Manager.AddComponent <CellDeath>(entity).Initialize(true); Manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); // Make it destructible. var health = Manager.AddComponent <Health>(entity); health.Value = health.MaxValue = 200 * scale; health.Regeneration = 0f; // As they don't move on their own, start asteroids as sleeping to save performance. body.IsAwake = false; }
public static RectangleF NextRectangle(this IUniformRandom random, float area, float minSize, float maxSize) { var rect = new RectangleF { Width = (float)random.NextDouble(minSize, maxSize), Height = (float)random.NextDouble(minSize, maxSize) }; rect.X = (float)random.NextDouble(-area / 2, area / 2 - rect.Width); rect.Y = (float)random.NextDouble(-area / 2, area / 2 - rect.Height); return(rect); }
/// <summary>Samples the offset of this sun.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled offset.</returns> private Vector2 SampleOffset(IUniformRandom random) { if (_offsetRadius != null && random != null) { Vector2 offset; offset.X = (float)(random.NextDouble() - 0.5); offset.Y = (float)(random.NextDouble() - 0.5); offset.Normalize(); offset *= MathHelper.Lerp(_offsetRadius.Low, _offsetRadius.High, (float)random.NextDouble()); return(offset); } return(Vector2.Zero); }
/// <summary>Samples the angle of this planet's orbit.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled angle.</returns> private float SampleAngle(IUniformRandom random) { return (MathHelper.ToRadians( (random == null) ? _angle.Low : MathHelper.Lerp(_angle.Low, _angle.High, (float)random.NextDouble()))); }
/// <summary>Samples the mass of this sun.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled mass.</returns> private float SampleMass(IUniformRandom random) { if (_mass != null) { return((random == null) ? _mass.Low : MathHelper.Lerp(_mass.Low, _mass.High, (float)random.NextDouble())); } return(0f); }
/// <summary>Get the next sample value from the gaussian distribution.</summary> public double NextSample() { for (;;) { // Select box at random. var u = (byte)_rng.NextUInt32(); var i = u & 0x7F; var sign = ((u & 0x80) == 0) ? -1.0 : 1.0; // Generate uniform random value with range [0,0xFFFFFFFF]. var u2 = _rng.NextUInt32(); // Special case for the base segment. if (0 == i) { if (u2 < _xComp[0]) { // Generated x is within R0. return(u2 * UIntToU * _aDivY0 * sign); } // Generated x is in the tail of the distribution. return(SampleTail() * sign); } // All other segments. if (u2 < _xComp[i]) { // Generated x is within the rectangle. return(u2 * UIntToU * _x[i] * sign); } // Generated x is outside of the rectangle. // Generate a random y coordinate and test if our (x,y) is within the distribution curve. // This execution path is relatively slow/expensive (makes a call to Math.Exp()) but relatively rarely executed, // although more often than the 'tail' path (above). double x = u2 * UIntToU * _x[i]; if (_y[i - 1] + ((_y[i] - _y[i - 1]) * _rng.NextDouble()) < GaussianPdfDenorm(x)) { return(x * sign); } } }
/// <summary>Samples the attributes to apply to the item.</summary> /// <param name="manager">The manager.</param> /// <param name="random">The randomizer to use.</param> /// <return>The entity with the attributes applied.</return> public int Sample(IManager manager, IUniformRandom random) { var entity = manager.AddEntity(); // Sample all values in advance, to allow reshuffling component creation // order in case we need to, without influencing the 'random' results. var radius = UnitConversion.ToSimulationUnits(SampleRadius(random)); var rotationSpeed = SampleRotationSpeed(random); var mass = SampleMass(random); var surfaceRotation = random != null ? (Math.Sign(random.NextDouble() - 0.5) * rotationSpeed) : rotationSpeed; // Give it a position. manager.AddComponent <Transform>(entity); // Add it to some indexes. var bounds = new FarRectangle(-radius, -radius, radius * 2, radius * 2); // Can be detected. manager.AddComponent <Indexable>(entity).Initialize(bounds, Detectable.IndexId); // Can make some noise. manager.AddComponent <Indexable>(entity).Initialize(SoundSystem.IndexId); // Must be detectable by the camera. manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); // Remove when out of bounds or large containing cell dies. manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); manager.AddComponent <CellDeath>(entity).Initialize(false); // Make it rotate. manager.AddComponent <Velocity>(entity).Initialize( Vector2.Zero, MathHelper.ToRadians(rotationSpeed) / Settings.TicksPerSecond); // Make it attract stuff if it has mass. if (mass > 0) { manager.AddComponent <Gravitation>(entity).Initialize(Gravitation.GravitationTypes.Attractor, mass); } // Make it detectable. manager.AddComponent <Detectable>(entity).Initialize("Textures/Radar/Icons/radar_planet"); // Make it visible. manager.AddComponent <PlanetRenderer>(entity).Initialize(this, UnitConversion.ToScreenUnits(radius), surfaceRotation); // Let it rap. manager.AddComponent <Sound>(entity).Initialize("Planet"); return(entity); }
/// <summary>Samples an attribute modifier from this constraint.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled attribute modifier.</returns> public AttributeModifier <TAttribute> SampleAttributeModifier(IUniformRandom random) { // Only randomize if necessary. var value = (random == null) ? _value.Low : MathHelper.Lerp(_value.Low, _value.High, (float)random.NextDouble()); if (_round) { value = (float)System.Math.Round(value); } return(new AttributeModifier <TAttribute>(_type, value, _computationType)); }
/// <summary>Samples the acceleration force.</summary> /// <param name="baseRotation">The base rotation.</param> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled acceleration force.</returns> public Vector2 SampleAccelerationForce(float baseRotation, IUniformRandom random) { if (_accelerationDirection != null && _accelerationForce != null) { var acceleration = Vector2.UnitX; var rotation = Matrix.CreateRotationZ( baseRotation + MathHelper.ToRadians( MathHelper.Lerp( _accelerationDirection.Low, _accelerationDirection.High, (random == null) ? 0 : (float)random.NextDouble()))); Vector2.Transform(ref acceleration, ref rotation, out acceleration); acceleration.Normalize(); return(acceleration * ((random == null) ? _accelerationForce.Low : MathHelper.Lerp( _accelerationForce.Low, _accelerationForce.High, (float)random.NextDouble()))); } return(Vector2.Zero); }
/// <summary>Samples the initial directed velocity.</summary> /// <param name="baseRotation">The base rotation.</param> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled velocity.</returns> public Vector2 SampleInitialDirectedVelocity(float baseRotation, IUniformRandom random) { if (_initialDirection != null && _initialVelocity != null) { var velocity = Vector2.UnitX; var rotation = Matrix.CreateRotationZ( baseRotation + MathHelper.ToRadians( MathHelper.Lerp( _initialDirection.Low, _initialDirection.High, (random == null) ? 0 : (float)random.NextDouble()))); Vector2.Transform(ref velocity, ref rotation, out velocity); velocity.Normalize(); return(velocity * ((random == null) ? _initialVelocity.Low : MathHelper.Lerp(_initialVelocity.Low, _initialVelocity.High, (float)random.NextDouble()))); } return(Vector2.Zero); }
/// <summary> /// Samples the specified number of attributes from the list of available attributes in the specified attribute /// pools. /// </summary> /// <param name="count">The number of attributes to sample.</param> /// <param name="random">The randomizer to use.</param> protected IEnumerable <AttributeModifier <AttributeType> > SampleAttributes(int count, IUniformRandom random) { if (count <= 0) { yield break; } // Get the cumulative weights, and figure out how many attributes // there are, so we don't have to resize our list of all attributes // while adding them. var summedWeights = 0; var attributeCount = 0; for (var i = 0; i < _additionalAttributes.Length; i++) { var pool = FactoryLibrary.GetAttributePool(_additionalAttributes[i]); attributeCount += pool.Attributes.Length; summedWeights += pool.Attributes.Sum(attribute => attribute.Weight); } // Get the actual list of available attributes. var attributes = new List <AttributePool.AttributeInfo>(attributeCount); for (var i = 0; i < _additionalAttributes.Length; i++) { attributes.AddRange(FactoryLibrary.GetAttributePool(_additionalAttributes[i]).Attributes); } // Sample some attributes! Make sure we always have some (may // remove some, if they may not be re-used). for (var i = 0; i < count && attributes.Count > 0; i++) { // Regarding the following... // Assume we have 5 attributes, when concatenating their // weights on an interval that might look like so: // |--|----|--|-|---------| // where one '-' represents one weight (i.e. '--' == 2). // Now, when sampling we multiply a uniform random value // with the sum of those weights, meaning that number // falls somewhere in that interval. What we need to do // then, is to figure out into which sub-interval it is, // meaning which attribute is to be picked. We do this by // starting with the left interval, moving to the right // and subtracting the weight of the current interval until // the remaining rolled value becomes negative, in which // case we it fell into the last one. (equal zero does // not count, because the roll is at max weight - 1, // because 0 counts for the first interval). // Note that the list does *not* have to be sorted for // this, because each point on the interval is equally // likely to be hit! // Get a random number determining the attribute we want. var roll = (int)(random.NextDouble() * summedWeights); // Figure out the interval, starting with the first. var j = 0; while (roll >= 0) { roll -= attributes[j].Weight; ++j; } // Get the attribute that was sampled. var attribute = attributes[j]; // Sample it! yield return(attribute.Attribute.SampleAttributeModifier(random)); // If the attribute may not be reused, remove it from our list. if (!attribute.AllowRedraw) { attributes.RemoveAt(j); summedWeights -= attribute.Weight; } } }
/// <summary>Samples the rotation speed of this planet.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled rotation speed.</returns> private float SampleRotationSpeed(IUniformRandom random) { if (_rotationSpeed != null) { return(MathHelper.ToRadians( (random == null) ? _rotationSpeed.Low : MathHelper.Lerp(_rotationSpeed.Low, _rotationSpeed.High, (float)random.NextDouble()))); } return(0f); }
/// <summary>Samples the eccentricity of this planet's orbit.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled major radius.</returns> private float SampleEccentricity(IUniformRandom random) { if (_eccentricity != null) { return((random == null) ? _eccentricity.Low : MathHelper.Lerp(_eccentricity.Low, _eccentricity.High, (float)random.NextDouble())); } return(0f); }
/// <summary>Samples the travel speed of this planet.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled travel speed.</returns> private float SampleTravelSpeed(IUniformRandom random) { if (_travelSpeed != null) { return((random == null) ? _travelSpeed.Low : MathHelper.Lerp(_travelSpeed.Low, _travelSpeed.High, (float)random.NextDouble())); } return(0f); }
/// <summary>Samples the angle of this planet's orbit.</summary> /// <param name="random">The randomizer to use.</param> /// <returns>The sampled angle.</returns> private float SampleOrbitRadius(IUniformRandom random) { return((random == null) ? _orbitRadius.Low : MathHelper.Lerp(_orbitRadius.Low, _orbitRadius.High, (float)random.NextDouble())); }
public static FarPosition NextVector(this IUniformRandom random, float area) { return(new FarPosition((float)(random.NextDouble() * area - area / 2.0), (float)(random.NextDouble() * area - area / 2.0))); }
public static Vector2 NextVector(this IUniformRandom random, float area) { return(new Vector2((float)(random.NextDouble() * area - area / 2.0), (float)(random.NextDouble() * area - area / 2.0))); }
/// <summary>Samples the attributes to apply to the item.</summary> /// <param name="manager">The manager.</param> /// <param name="cellCenter">The center of the cell the sun will be inserted in.</param> /// <param name="random">The randomizer to use.</param> /// <return>The entity with the attributes applied.</return> public int Sample(IManager manager, FarPosition cellCenter, IUniformRandom random) { var entity = manager.AddEntity(); // Sample all values in advance, to allow reshuffling component creation // order in case we need to, without influencing the 'random' results. var radius = UnitConversion.ToSimulationUnits(SampleRadius(random)); var offset = SampleOffset(random); var mass = SampleMass(random); var surfaceRotation = -Vector2.UnitX; if (random != null) { surfaceRotation.X = (float)(random.NextDouble() - 0.5) * 2; surfaceRotation.Y = (float)(random.NextDouble() - 0.5) * 2; surfaceRotation.Normalize(); } var primaryTurbulenceRotation = Vector2.UnitX; if (random != null) { primaryTurbulenceRotation.X = (float)(random.NextDouble() - 0.5) * 2; primaryTurbulenceRotation.Y = (float)(random.NextDouble() - 0.5) * 2; primaryTurbulenceRotation.Normalize(); } var secondaryTurbulenceRotation = Vector2.UnitY; if (random != null) { secondaryTurbulenceRotation.X = (float)(random.NextDouble() - 0.5) * 2; secondaryTurbulenceRotation.Y = (float)(random.NextDouble() - 0.5) * 2; secondaryTurbulenceRotation.Normalize(); } var body = manager.AddBody(entity, offset + cellCenter); manager.AttachCircle(body, radius); var bounds = new FarRectangle(-radius, -radius, radius * 2, radius * 2); // Can be detected. manager.AddComponent <Indexable>(entity).Initialize(bounds, Detectable.IndexId); // Can make noise. manager.AddComponent <Indexable>(entity).Initialize(SoundSystem.IndexId); // Must be detectable by the camera. manager.AddComponent <Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); // Remove when out of bounds or large containing cell dies. manager.AddComponent <Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); manager.AddComponent <CellDeath>(entity).Initialize(false); // Make it attract stuff if it has mass. if (mass > 0) { manager.AddComponent <Gravitation>(entity).Initialize(Gravitation.GravitationTypes.Attractor); } // Damage stuff that touches a sun. manager.AddComponent <CollisionDamage>(entity).Initialize(false); var attributes = manager.AddComponent <Attributes <AttributeType> >(entity); attributes.SetBaseValue(AttributeType.AttackBurnMinDamage, 1000); attributes.SetBaseValue(AttributeType.AttackBurnMaxDamage, 2000); attributes.SetBaseValue(AttributeType.AttackBurnChance, 1); attributes.SetBaseValue(AttributeType.AttackBurnMinDuration, 5); attributes.SetBaseValue(AttributeType.AttackBurnMaxDuration, 5); attributes.SetBaseValue(AttributeType.Mass, mass); // Make it detectable. manager.AddComponent <Detectable>(entity).Initialize("Textures/Radar/Icons/radar_sun"); // Make it glow. manager.AddComponent <SunRenderer>(entity) .Initialize(UnitConversion.ToScreenUnits(radius) * 0.95f, surfaceRotation, primaryTurbulenceRotation, secondaryTurbulenceRotation, _tint); // Make it go whoooosh. manager.AddComponent <Sound>(entity).Initialize("Sun"); return(entity); }
private void PopulateSubCell(CellStateChanged message, IUniformRandom random) { // We randomly spawn some asteroid fields in sub-cells. These are laid out as follows: // - we decide how many fields we want to spawn. // - we build a grid inside the cell that has enough entries for our asteroid fields, // i.e. we take the next higher squared number (e.g. if we have 5 fields, we generate // a 3x3 grid). // - For each field we randomly pick one such grid cell to determine it's basic position, // and shift it to a random position inside that grid cell. // - To position the actual asteroids, we want it to be somewhat circular, but it should // not look too regular. We lay out our asteroids in a spiral, on which we place our // asteroids in a fixed interval. Finally we add some random offset to make the spiral // non-obvious. // Get content manager to fetch textures from which to generate physics models. var content = ((ContentSystem)Manager.GetSystem(ContentSystem.TypeId)).Content; // Number of asteroid fields in this cell? var fieldCount = random.NextInt32(8, 12); // Determine number of rows and cols for base positions. var cols = (int)Math.Ceiling(Math.Sqrt(fieldCount)); // Build sampling list. var cells = new List <Tuple <int, int> >(cols * cols); for (var x = 0; x < cols; ++x) { for (var y = 0; y < cols; ++y) { cells.Add(Tuple.Create(x, y)); } } // Size of a cell in our sub grid. var gridSize = CellSystem.SubCellSize / (float)cols; // Cell top left corner position. var cellPosition = new FarPosition(message.X, message.Y) * CellSystem.SubCellSize; // Generate asteroid fields. for (var i = 0; i < fieldCount; ++i) { // Get base position. var positionIndex = random.NextInt32(cells.Count); var fieldIndex = cells[positionIndex]; cells.RemoveAt(positionIndex); var asteroidCount = random.NextInt32(30, 60); var center = cellPosition + new FarPosition( fieldIndex.Item1 * gridSize + (float)random.NextDouble(0, gridSize), fieldIndex.Item2 * gridSize + (float)random.NextDouble(0, gridSize)); // We grow our asteroid fields as spirals, with a little jitter. const float jitter = 2.5f; const float radiusStep = 0.4f; //< how fast we move outwards. const float angleStep = 2.25f; //< the asteroid interval on the spiral. var theta = angleStep / radiusStep; // Create first one at the center. CreateAsteroid(center, random, content); // Generate rest of the spiral. for (var j = 1; j < asteroidCount; ++j) { // Compute position in our spiral. var radius = radiusStep * theta; var position = center; position.X += (float)Math.Cos(theta) * radius; position.Y += (float)Math.Sin(theta) * radius; position.X += (float)random.NextDouble(-jitter / 2, jitter / 2); position.Y += (float)random.NextDouble(-jitter / 2, jitter / 2); theta += angleStep / radius; CreateAsteroid(position, random, content); } } //// Sprinkle some asteroids in the background, for depth and movement perception. //for (var i = 0; i < 1000; ++i) //{ // var center = cellPosition + new FarPosition( // (float) random.NextDouble(0, CellSystem.SubCellSize), // (float) random.NextDouble(0, CellSystem.SubCellSize)); // // Randomly scale and rotate it. // var layer = (float) random.NextDouble(0.5f, 0.75f); // var scale = (float) random.NextDouble(0.6f, 0.9f); // var angle = (float) random.NextDouble() * MathHelper.TwoPi; // // Determine shape for physics system. // var textureName = "Textures/Asteroids/rock_" + random.NextInt32(1, 14); // var texture = content.Load<Texture2D>(textureName); // // Create component that will be rendered. // var entity = Manager.AddEntity(); // Manager.AddComponent<Transform>(entity).Initialize(center, angle); // // Expand bounds based on layer for camera and interpolation system, so that they // // can see the asteroids in time (lower parallax = farther in background = moving // // slower = longer in screen = wider actual viewport necessary to check). // var width = UnitConversion.ToSimulationUnits(texture.Width); // var height = UnitConversion.ToSimulationUnits(texture.Height); // var diagonal = 8f * (float) Math.Sqrt(width * width + height * height) / layer / scale; // var bounds = new FarRectangle(-diagonal / 2f, -diagonal / 2f, diagonal, diagonal); // // Rendering stuff. // Manager.AddComponent<Parallax>(entity).Initialize(layer); // Manager.AddComponent<Indexable>(entity).Initialize(bounds, CameraSystem.IndexId); // Manager.AddComponent<Indexable>(entity).Initialize(bounds, InterpolationSystem.IndexId); // Manager.AddComponent<SimpleTextureDrawable>(entity).Initialize(textureName, new Color(100, 100, 100, 255), scale); // // Auto removal. // Manager.AddComponent<CellDeath>(entity).Initialize(true); // Manager.AddComponent<Indexable>(entity).Initialize(CellSystem.CellDeathAutoRemoveIndexId); //} }