예제 #1
0
        // 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;
        }
예제 #2
0
 /// <summary>
 ///     Shuffles the specified list using the Fisher-Yates shuffle. This only shuffles a segment in a list, as opposed
 ///     to shuffling the complete list.
 /// </summary>
 /// <typeparam name="T">The type of element stored in the list.</typeparam>
 /// <param name="list">The list to shuffle.</param>
 /// <param name="offset">The offset at which to start shuffling.</param>
 /// <param name="length">The length of the interval to shuffle.</param>
 /// <param name="random">The random number generator to use for shuffling. If none is specified, a new one will be created.</param>
 public static void Shuffle <T>(this IList <T> list, int offset, int length, IUniformRandom random = null)
 {
     random = random ?? new MersenneTwister();
     for (var i = offset + length - 1; i > offset; --i)
     {
         var j = random.NextInt32(i + 1);
         var t = list[j];
         list[j] = list[i];
         list[i] = t;
     }
 }
예제 #3
0
        /// <summary>Updates the behavior and returns the behavior type to switch to.</summary>
        public void Update()
        {
            // Don't update more often than we have to. For example, patrol
            // behaviors should require way fewer updates than attack
            // behaviors.
            if (_ticksToWait > 0)
            {
                --_ticksToWait;
                return;
            }

            // Do the behavior specific update.
            if (!UpdateInternal())
            {
                // Skip if we don't have to do the rest (e.g. popped self).
                return;
            }

            // No change, wait a bit with the next update.
            _ticksToWait = _pollRate / 2 + Random.NextInt32(_pollRate);

            // Figure out where we want to go.
            var targetPosition = GetTargetPosition();

            // And accordingly, which way to accelerate to get there.
            var direction = (Vector2)(targetPosition - ((ITransform)AI.Manager.GetComponent(AI.Entity, TransformTypeId)).Position);

            // Normalize if it's not zero.
            var norm = direction.LengthSquared();

            if (norm > 0)
            {
                norm         = (float)Math.Sqrt(norm);
                direction.X /= norm;
                direction.Y /= norm;
            }

            // Multiply with the desired acceleration.
            var speed = MathHelper.Clamp(GetThrusterPower(), 0, 1);

            direction.X *= speed;
            direction.Y *= speed;

            // Figure out where we want to go vegetatively (flocking).
            direction += GetVegetativeDirection() * AI.Configuration.VegetativeWeight;

            // Set our new acceleration direction and target rotation.
            var shipControl = ((ShipControl)AI.Manager.GetComponent(AI.Entity, ShipControl.TypeId));

            shipControl.SetAcceleration(direction);
            shipControl.SetTargetRotation(GetTargetRotation(direction));
        }
예제 #4
0
 /// <summary>Samples the local attribute count.</summary>
 /// <param name="random">The randomizer to use.</param>
 /// <returns></returns>
 private int SampleLocalAttributeCount(IUniformRandom random)
 {
     return((_additionalLocalAttributeCount.Low == _additionalLocalAttributeCount.High || random == null)
                ? _additionalLocalAttributeCount.Low
                : random.NextInt32(_additionalLocalAttributeCount.Low, _additionalLocalAttributeCount.High));
 }
예제 #5
0
        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);
            //}
        }