protected override void CalculateLines() { float w = Scale.X / 2.0f; float h = Scale.Y / 2.0f; // the first one is left-top, clock-wise Vector3[] vertices = new Vector3[4]; vertices[0] = new Vector3(-w, -h, 0.0f); vertices[1] = new Vector3(w, -h, 0.0f); vertices[2] = new Vector3(w, h, 0.0f); vertices[3] = new Vector3(-w, h, 0.0f); Quaternion rotation = Quaternion.BetweenDirections(Vector3.UnitZ, normal); for (int i = 0; i < vertices.Length; i++) { rotation.Rotate(ref vertices[i]); vertices[i] += Transform.Position; } // contour lines[0] = new Line(vertices[0], vertices[1], Color); lines[1] = new Line(vertices[1], vertices[2], Color); lines[2] = new Line(vertices[2], vertices[3], Color); lines[3] = new Line(vertices[3], vertices[0], Color); // cross lines[4] = new Line(vertices[0], vertices[2], Color); lines[5] = new Line(vertices[1], vertices[3], Color); NotifyPropertyChanged(); }
public static (VertexPositionTexture[] Vertices, int[] Indices) GenerateQuad(float width, float height) { var quadMeshData = GeometricPrimitive.Plane.New(width, height); VertexPositionTexture[] vertices = new VertexPositionTexture[quadMeshData.Vertices.Length]; int[] indices = new int[quadMeshData.Indices.Length]; CopyFromGeometricPrimitive(quadMeshData, ref vertices, ref indices); /* transform it because in its default orientation it isnt flat to the normal up */ Quaternion rotation = Quaternion.BetweenDirections(Vector3.UnitZ, Vector3.UnitY); for (int i = 0; i < vertices.Length; ++i) { vertices[i].Position = Vector3.Transform(vertices[i].Position, rotation); } return(vertices, indices); }
protected override async Task Shoot(Entity targetEntity) { // Play shooting animation Drone.Animation.Shoot(); // Wait a bit await Task.Delay(TimeSpan.FromSeconds(AnimationDelay)); // Spawn projectile var projectileEntity = ProjectilePrefab.Instantiate().Single(); var projectile = projectileEntity.Get <Projectile>(); projectile.Owner = Drone.Entity; projectileEntity.Transform.Position = ProjectileSpawnPoint.Transform.WorldMatrix.TranslationVector; projectileEntity.Transform.Rotation = Quaternion.BetweenDirections(Vector3.UnitZ, Drone.HeadDirection); Drone.SceneSystem.SceneInstance.RootScene.Entities.Add(projectile.Entity); projectile.SetDirection(Drone.HeadDirection); shootSoundSelector.PlayAndForget(); }
public override void Update() { Vector3 targetDir = TargetPosition - Entity.Transform.WorldMatrix.TranslationVector; if (targetDir.LengthSquared() < 1.0f) { return; } targetDir.Normalize(); var projectile = Entity.Get <Projectile>(); var currentDirection = Vector3.Normalize(projectile.Rigidbody.LinearVelocity); // Bend towards target direction targetDir = currentDirection + Vector3.Normalize(targetDir) * HomingSpeed * (float)Game.UpdateTime.Elapsed.TotalSeconds; targetDir.Normalize(); // Reset velocity based on adjusted direction projectile.Rigidbody.LinearVelocity = Vector3.Zero; projectile.SetDirection(targetDir); projectile.Entity.Transform.Rotation = Quaternion.BetweenDirections(Vector3.UnitZ, targetDir); }
public override async Task Execute() { while (Game.IsRunning) { var target = await weaponFiredEvent.ReceiveAsync(); if (target.DidFire) { SpawnEvent("MuzzleFlash", Entity, Matrix.Identity); } if (target.DidHit) { SpawnEvent("BulletImpact", null, Matrix.RotationQuaternion(Quaternion.BetweenDirections(Vector3.UnitY, target.HitResult.Normal)) * Matrix.Translation(target.HitResult.Point)); } if (target.HitResult.Collider is RigidbodyComponent rigidBody) { var rand = new Random(); SpawnEvent("DamagedTrail", rigidBody.Entity, Matrix.Translation(new Vector3((float)rand.NextDouble() - 0.5f, (float)rand.NextDouble() - 0.5f, (float)rand.NextDouble() - 0.5f))); } } }
/// <summary> /// Update and recreate a page if necessary /// </summary> private void UpdatePages(TerrainComponent terrain, TerrainVegetationComponent component, TerrainVegetationRenderData renderData) { if (renderData.Pages != null && renderData.MaskImage != null && !component.IsDirty) { return; } if (renderData.MaskImage == null || renderData.Mask != component.Mask) { renderData.Mask = component.Mask; renderData.MaskImage?.Dispose(); // Get mask image data try { var game = Services.GetService <IGame>(); var graphicsContext = game.GraphicsContext; var commandList = graphicsContext.CommandList; renderData.MaskImage = component.Mask.GetDataAsImage(commandList); } catch { // Image probably not loaded yet .. try again next frame :) return; } } // Cache render data so we won't need to recreate pages var mask = renderData.MaskImage.PixelBuffer[0]; var maskChannel = (int)component.MaskChannel; // Calculate terrain center offset var terrainOffset = terrain.Size / 2.0f; // Create vegetation pages var rng = new Random(component.Seed); var pagesPerRow = (int)terrain.Size / PageSize; var instancesPerRow = (int)(PageSize * component.Density); var distancePerInstance = PageSize / (float)instancesPerRow; renderData.Pages = new TerrainVegetationPage[pagesPerRow * pagesPerRow]; var scaleRange = component.MaxScale - component.MinScale; for (var pz = 0; pz < pagesPerRow; pz++) { for (var px = 0; px < pagesPerRow; px++) { var radius = PageSize * 0.5f; var pagePosition = new Vector3(px * PageSize - terrainOffset, 0, pz * PageSize - terrainOffset); var page = new TerrainVegetationPage(); renderData.Pages[pz * pagesPerRow + px] = page; for (var iz = 0; iz < instancesPerRow; iz++) { for (var ix = 0; ix < instancesPerRow; ix++) { var position = pagePosition; position.X += ix * distancePerInstance; position.Z += iz * distancePerInstance; position.X += (float)rng.NextDouble() * distancePerInstance * 2.0f - distancePerInstance; position.Z += (float)rng.NextDouble() * distancePerInstance * 2.0f - distancePerInstance; position.Y = terrain.GetHeightAt(position.X, position.Z); var tx = (int)((position.X + terrainOffset) / terrain.Size * mask.Width); var ty = (int)((position.Z + terrainOffset) / terrain.Size * mask.Height); if (tx < 0 || tx >= mask.Width || ty < 0 || ty >= mask.Height) { continue; } var maskDensity = mask.GetPixel <Color>(tx, ty)[maskChannel] / 255.0; if (rng.NextDouble() > maskDensity) { continue; } var normal = terrain.GetNormalAt(position.X, position.Z); var slope = 1.0f - Math.Abs(normal.Y); if (slope < component.MinSlope || slope > component.MaxSlope) { continue; } var scale = (float)rng.NextDouble() * scaleRange + component.MinScale; var rotation = Quaternion.RotationAxis(Vector3.UnitY, (float)rng.NextDouble() * MathUtil.TwoPi) * Quaternion.BetweenDirections(Vector3.UnitY, normal); var scaling = new Vector3(scale); Matrix.Transformation(ref scaling, ref rotation, ref position, out var transformation); page.Instances.Add(transformation); } } page.WorldPosition = pagePosition + new Vector3(radius, 0, radius); } } component.IsDirty = false; }
public override void Update() { if (Input.HasMouse) { ClickResult clickResult; Utils.ScreenPositionToWorldPositionRaycast(Input.MousePosition, Camera, this.GetSimulation(), out clickResult); bool isMoving = (Input.IsMouseButtonDown(MouseButton.Left) && lastClickResult.Type == ClickType.Ground && clickResult.Type == ClickType.Ground); bool isHighlit = (!isMoving && clickResult.Type == ClickType.Container); // Character continuous moving if (isMoving) { lastClickResult.WorldPosition = clickResult.WorldPosition; MoveDestinationEventKey.Broadcast(lastClickResult); } // Object highlighting if (isHighlit) { ModelComponent modelComponentA = Highlight?.Get <ModelComponent>(); ModelComponent modelComponentB = clickResult.ClickedEntity.Get <ModelComponent>(); if (modelComponentA != null && modelComponentB != null) { int materialCount = modelComponentB.Model.Materials.Count; modelComponentA.Model = modelComponentB.Model; modelComponentA.Materials.Clear(); for (int i = 0; i < materialCount; i++) { modelComponentA.Materials.Add(i, HighlightMaterial); } modelComponentA.Entity.Transform.UseTRS = false; modelComponentA.Entity.Transform.LocalMatrix = modelComponentB.Entity.Transform.WorldMatrix; } } else { ModelComponent modelComponentA = Highlight?.Get <ModelComponent>(); if (modelComponentA != null) { modelComponentA.Entity.Transform.LocalMatrix = Matrix.Scaling(0); } } } // Mouse-based camera rotation. Only enabled after you click the screen to lock your cursor, pressing escape cancels this foreach (PointerEvent pointerEvent in Input.PointerEvents.Where(x => x.EventType == PointerEventType.Pressed)) { ClickResult clickResult; if (Utils.ScreenPositionToWorldPositionRaycast(pointerEvent.Position, Camera, this.GetSimulation(), out clickResult)) { lastClickResult = clickResult; MoveDestinationEventKey.Broadcast(clickResult); if (ClickEffect != null && clickResult.Type == ClickType.Ground) { this.SpawnPrefabInstance(ClickEffect, null, 1.2f, Matrix.RotationQuaternion(Quaternion.BetweenDirections(Vector3.UnitY, clickResult.HitResult.Normal)) * Matrix.Translation(clickResult.WorldPosition)); } } } }
public override void Update() { // HACK: should probably not rely on accessing this UI component directly. if (_inGameScreenPageHandler == null) { var uiManager = SceneSystem.GetUIManagerFromRootScene(); _inGameScreenPageHandler = uiManager.TopPageHandler as InGameScreenPageHandler; if (_inGameScreenPageHandler == null) { // UI not loaded yet return; } } if (!_inGameScreenPageHandler.IsTopMostScreen) { // Don't process any input, since it's probably in a sub-menu! return; } if (Input.HasMouse) { ClickResult clickResult; Utils.ScreenPositionToWorldPositionRaycast(Input.MousePosition, Camera, this.GetSimulation(), out clickResult); var isMoving = (Input.IsMouseButtonDown(MouseButton.Left) && lastClickResult.Type == ClickType.Ground && clickResult.Type == ClickType.Ground); var isHighlit = (!isMoving && clickResult.Type == ClickType.LootCrate); // Character continuous moving if (isMoving) { lastClickResult.WorldPosition = clickResult.WorldPosition; MoveDestinationEventKey.Broadcast(lastClickResult); } // Object highlighting if (isHighlit) { var modelComponentA = Highlight?.Get <ModelComponent>(); var modelComponentB = clickResult.ClickedEntity.Get <ModelComponent>(); if (modelComponentA != null && modelComponentB != null) { var materialCount = modelComponentB.Model.Materials.Count; modelComponentA.Model = modelComponentB.Model; modelComponentA.Materials.Clear(); for (int i = 0; i < materialCount; i++) { modelComponentA.Materials.Add(i, HighlightMaterial); } modelComponentA.Entity.Transform.UseTRS = false; modelComponentA.Entity.Transform.LocalMatrix = modelComponentB.Entity.Transform.WorldMatrix; } } else { var modelComponentA = Highlight?.Get <ModelComponent>(); if (modelComponentA != null) { modelComponentA.Entity.Transform.LocalMatrix = Matrix.Scaling(0); } } } // Mouse-based camera rotation. Only enabled after you click the screen to lock your cursor, pressing escape cancels this foreach (var pointerEvent in Input.PointerEvents.Where(x => x.EventType == PointerEventType.Pressed)) { ClickResult clickResult; if (Utils.ScreenPositionToWorldPositionRaycast(pointerEvent.Position, Camera, this.GetSimulation(), out clickResult)) { lastClickResult = clickResult; MoveDestinationEventKey.Broadcast(clickResult); if (ClickEffect != null && clickResult.Type == ClickType.Ground) { this.SpawnPrefabInstance(ClickEffect, null, 1.2f, Matrix.RotationQuaternion(Quaternion.BetweenDirections(Vector3.UnitY, clickResult.HitResult.Normal)) * Matrix.Translation(clickResult.WorldPosition)); } } } }
/// <summary> /// Spawns rockets in a grid /// </summary> protected override async Task Shoot(Entity targetEntity) { // Play shooting animation Drone.Animation.Shoot(); // Wait a bit await Task.Delay(TimeSpan.FromSeconds(AnimationDelay)); List <Projectile> projectiles = new List <Projectile>(); Vector2 stepOffset = new Vector2(1.0f / ArraySize.X, 1.0f / ArraySize.Y) * ArrayExtent; // Generate random spawn position List <Vector2> spawnOffsets = new List <Vector2>(); for (int y = 0; y < ArraySize.Y; y++) { float stepY = (ArraySize.Y == 0) ? 0.0f : (y / (float)(ArraySize.Y - 1) * 2.0f - 1.0f); for (int x = 0; x < ArraySize.X; x++) { float stepX = (ArraySize.X == 0) ? 0.0f : (x / (float)(ArraySize.X - 1) * 2.0f - 1.0f); Vector2 spawnOffset = new Vector2((stepOffset.X * stepX), (stepOffset.Y * stepY)); spawnOffsets.Add(spawnOffset); } } // Spawn in random order while (spawnOffsets.Count > 0) { // Recalculate directions and start position since drone might have moved var position = ProjectileSpawnPoint.Transform.WorldMatrix.TranslationVector; Vector3 up = Drone.Entity.Transform.WorldMatrix.Up; Vector3 aimDirection = Drone.HeadDirection; Vector3 right = Vector3.Normalize(Vector3.Cross(up, aimDirection)); // Retrieve spawn position for this missile based on the offsets int targetPositionIndex = random.Next(0, spawnOffsets.Count - 1); Vector2 spawnOffset = spawnOffsets[targetPositionIndex]; Vector3 spawnPosition = position + spawnOffset.X * right + spawnOffset.Y * up; spawnPosition += aimDirection * ((float)random.NextDouble() - 0.5f) * 0.1f; spawnOffsets.RemoveAt(targetPositionIndex); // Spawn rocket var projectileEntity = ProjectilePrefab.Instantiate().Single(); var projectile = projectileEntity.Get <Projectile>(); projectile.Owner = Drone.Entity; projectiles.Add(projectile); projectileEntity.Transform.Position = spawnPosition; projectileEntity.Transform.Rotation = Quaternion.BetweenDirections(Vector3.UnitZ, Drone.HeadDirection); // Random Pitch/Roll (relative to shooting direction) Vector2 randomDeviation = new Vector2((float)random.NextDouble(), (float)random.NextDouble()); randomDeviation.X = randomDeviation.X * SpreadAngle.Radians; // Pitch randomDeviation.Y *= MathUtil.TwoPi; // Roll projectileEntity.Transform.Rotation = projectileEntity.Transform.Rotation * Quaternion.RotationAxis(right, randomDeviation.X) * Quaternion.RotationAxis(aimDirection, randomDeviation.Y); var rocket = (projectile as RocketProjectile); if (targetEntity != null) { rocket?.SetTarget(targetEntity.Transform.WorldMatrix.TranslationVector); } else { rocket?.SetTarget(position + aimDirection * ShootingRange); } Drone.SceneSystem.SceneInstance.RootScene.Entities.Add(projectile.Entity); // Set initial direction Vector3 initialRocketDirection = Vector3.Transform(Vector3.UnitZ, projectileEntity.Transform.Rotation); projectile.SetDirection(initialRocketDirection); shootSound.PlayAndForget(); await Task.Delay(2 + random.Next(0, 8)); } }