public bool Intersects(FRectangle frectangle) { return(!(frectangle.Right < this.Left || frectangle.Left > this.Right || frectangle.Bottom < this.Top || frectangle.Top > this.Bottom)); }
public bool Contains(FRectangle frectangle) { return(!(frectangle.Right < this.Left || frectangle.Right > this.Right || frectangle.Left < this.Left || frectangle.Left > this.Right || frectangle.Top < this.Top || frectangle.Top > this.Bottom || frectangle.Bottom < this.Top || frectangle.Bottom > this.Bottom)); }
/// <summary> /// Checks if Two Entities are intersecting each other assuming the specified states, gameTime and group filter. /// This check is actually rather ineffecient. O(n^2) time complexity. In reality though, we would be smart about /// what to compare - hence the group and state filters. The number of comparasins between drawables should be kept /// to a minimum in order to maintain performance. /// </summary> public static bool IntersectsWith( Entity entity1, string entity1State, string entity1Group, Entity entity2, string entity2State, string entity2Group, GameTime gameTime ) { HashSet<GameDrawableInstance> entity1Instances = entity1.Drawables.GetByState(entity1State); HashSet<GameDrawableInstance> entity2Instances = entity2.Drawables.GetByState(entity2State); if (entity1Instances == null || entity2Instances == null) return false; foreach (GameDrawableInstance instanceForEntity1 in entity1Instances) { if (entity1Group == null || instanceForEntity1._associatedGroup == entity1Group) { Rectangle sourceRectangleEntity1 = instanceForEntity1.GetSourceRectangle(gameTime); float entity1SourceWidth = sourceRectangleEntity1.Width * entity1.ScaleX; float entity1SourceHeight = sourceRectangleEntity1.Height * entity1.ScaleY; FRectangle absBoundingRectEntity1 = new FRectangle( entity1.Pos.X - entity1SourceWidth * instanceForEntity1.Drawable.Origin.X, entity1.Pos.Y - entity1SourceHeight * instanceForEntity1.Drawable.Origin.Y, entity1SourceWidth, entity1SourceHeight ); foreach (GameDrawableInstance instanceForEntity2 in entity2Instances) { if (entity2Group == null || instanceForEntity2._associatedGroup == entity2Group) { Rectangle sourceRectangleEntity2 = instanceForEntity2.GetSourceRectangle(gameTime); float entity2SourceWidth = sourceRectangleEntity2.Width * entity2.ScaleX; float entity2SourceHeight = sourceRectangleEntity2.Height * entity2.ScaleY; FRectangle absBoundingRectEntity2 = new FRectangle( entity2.Pos.X - entity2SourceWidth * instanceForEntity2.Drawable.Origin.X, entity2.Pos.Y - entity2SourceHeight * instanceForEntity2.Drawable.Origin.Y, entity2SourceWidth, entity2SourceHeight ); // Check if the two bounding boxes intersect if (absBoundingRectEntity1.Intersects(absBoundingRectEntity2)) return true; } } } } // No Intersection tests passed. return false; }
internal List<List<Entity>> GetHashList(FRectangle boundingBox) { List<List<Entity>> result = new List<List<Entity>>(); int topLeftIndex = GetIndex(boundingBox.Left, boundingBox.Top); int topRightIndex = GetIndex(boundingBox.Right, boundingBox.Top); int bottomLeftIndex = GetIndex(boundingBox.Left, boundingBox.Bottom); int width = topRightIndex - topLeftIndex + 1; int height = (bottomLeftIndex - topLeftIndex) / _boxCountY + 1; for (int i = 0; i < width; i++) { for (int j = 0; j < height; j++) { int index = topLeftIndex + i + j * _boxCountX; result.Add(_hashLists[index]); } } return result; }
internal void Remove(FRectangle boundingBox, Entity entity) { foreach (List<Entity> entityList in GetHashList(boundingBox)) entityList.Remove(entity); }
public List<Entity> GetIntersectingEntities(FRectangle region) { return Collider.GetIntersectingEntites(region); }
public List<Entity> GetIntersectingEntites(FRectangle pxRegion) { List<Entity> result = new List<Entity>(); foreach (List<Entity> entities in GetHashList(pxRegion)) foreach (Entity entity in entities) if (!result.Contains(entity)) result.Add(entity); return result; }
public bool Intersects(FRectangle frectangle) { return !(frectangle.Right < this.Left || frectangle.Left > this.Right || frectangle.Bottom < this.Top || frectangle.Top > this.Bottom ); }
public ViewPortInfo DrawWorldViewPort(SpriteBatch spriteBatch, Vector2 center, float zoom, Rectangle destRectangle, Color color, SamplerState samplerState, SpriteFont spriteFont=null) { ViewPortInfo viewPortInfo = new ViewPortInfo(); { viewPortInfo.pxTileWidth = (int) Math.Ceiling(Map.TileWidth * zoom); viewPortInfo.pxTileHeight = (int) Math.Ceiling(Map.TileHeight * zoom); // Note about ActualZoom Property: // because there is a loss of data between to conversion from Map.pxTileWidth * Zoom -> (int) // we need to determine what was the actual level of zoom that was applied to the tiles and use that // this ensures that entities that will be drawn will be placed correctly on the screen. viewPortInfo.ActualZoom = viewPortInfo.pxTileWidth / Map.TileWidth; viewPortInfo.pxWidth = destRectangle.Width / viewPortInfo.ActualZoom; viewPortInfo.pxHeight = destRectangle.Height / viewPortInfo.ActualZoom; viewPortInfo.pxTopLeftX = center.X - viewPortInfo.pxWidth / 2.0f; viewPortInfo.pxTopLeftY = center.Y - viewPortInfo.pxHeight / 2.0f; viewPortInfo.TileCountX = (int) Math.Ceiling((double)viewPortInfo.pxWidth / Map.TileWidth) + 1; viewPortInfo.TileCountY = (int) Math.Ceiling((double)viewPortInfo.pxHeight / Map.TileHeight) + 1; // Prevent the View from going outisde of the WORLD coordinates. if (viewPortInfo.pxTopLeftX < 0) viewPortInfo.pxTopLeftX = 0; if (viewPortInfo.pxTopLeftY < 0) viewPortInfo.pxTopLeftY = 0; if (viewPortInfo.pxTopLeftX + viewPortInfo.pxWidth >= Map.pxWidth) viewPortInfo.pxTopLeftX = Map.pxWidth - viewPortInfo.pxWidth; if (viewPortInfo.pxTopLeftY + viewPortInfo.pxHeight >= Map.pxHeight) viewPortInfo.pxTopLeftY = Map.pxHeight - viewPortInfo.pxHeight; // Calculate any decimal displacement required (For Positions with decimal points). viewPortInfo.pxDispX = viewPortInfo.pxTopLeftX - ((int)viewPortInfo.pxTopLeftX / Map.TileWidth) * Map.TileWidth; viewPortInfo.pxDispY = viewPortInfo.pxTopLeftY - ((int)viewPortInfo.pxTopLeftY / Map.TileHeight) * Map.TileHeight; viewPortInfo.pxViewPortBounds = new Rectangle( (int) Math.Ceiling(viewPortInfo.pxTopLeftX), (int) Math.Ceiling(viewPortInfo.pxTopLeftY), (int) Math.Ceiling(viewPortInfo.pxWidth), (int) Math.Ceiling(viewPortInfo.pxHeight) ); } // RENDER THE GAME WORLD TO THE VIEWPORT RENDER TARGET GraphicsDevice.SetRenderTarget(_inputBuffer); GraphicsDevice.Clear(Map.Background); // DRAW THE WORLD MAP OverallPerformance.StartTiming("TileRenderingTime"); // Deferred Rendering should be fine for rendering tile as long as we draw tile layers one at a time spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, samplerState, null, null); { for (int layerIndex = 0; layerIndex < Map.TileLayers.Count; layerIndex++) { // DRAW EACH LAYER TileLayer tileLayer = Map.TileLayers[layerIndex]; if (tileLayer.Visible) { Color layerColor = new Color() { R = tileLayer.Color.R, G = tileLayer.Color.G, B = tileLayer.Color.B, A = (byte)(tileLayer.Color.A * tileLayer.Opacity) }; float depth = 1 - (layerIndex / 10000.0f); for (int i = 0; i < viewPortInfo.TileCountX; i++) { for (int j = 0; j < viewPortInfo.TileCountY; j++) { int tileX = (int)(i + viewPortInfo.pxTopLeftX / Map.TileWidth); int tileY = (int)(j + viewPortInfo.pxTopLeftY / Map.TileHeight); int tileGid = tileLayer[tileX, tileY]; Rectangle pxTileDestRect = new Rectangle( (int) Math.Ceiling(i * viewPortInfo.pxTileWidth - viewPortInfo.pxDispX * viewPortInfo.ActualZoom), (int) Math.Ceiling(j * viewPortInfo.pxTileHeight - viewPortInfo.pxDispY * viewPortInfo.ActualZoom), (int) viewPortInfo.pxTileWidth, (int) viewPortInfo.pxTileHeight ); if (tileGid != 0 && tileGid != -1) // NULL or INVALID Tile Gid is ignored { Tile tile = Map.Tiles[tileGid]; spriteBatch.Draw( tile.sourceTexture, pxTileDestRect, tile.SourceRectangle, layerColor, 0, Vector2.Zero, SpriteEffects.None, depth ); } // DRAW THE TILE LAYER GRID IF ENABLE if (DrawingOptions.ShowTileGrid && layerIndex == Map.TileLayers.Count - 1) spriteBatch.DrawRectangle(pxTileDestRect, Color.Black, 0); } } } } } spriteBatch.End(); OverallPerformance.StopTiming("TileRenderingTime"); // Calculate the entity Displacement caused by pxTopLeft at a global scale to prevent jittering. // Each entity should be displaced by the *same amount* based on the pxTopLeftX/Y values // this is to prevent entities 'jittering on the screen' when moving the camera. float globalDispX = (int) Math.Ceiling(viewPortInfo.pxTopLeftX * viewPortInfo.ActualZoom); float globalDispY = (int) Math.Ceiling(viewPortInfo.pxTopLeftY * viewPortInfo.ActualZoom); // DRAW VISIBLE REGISTERED ENTITIES OverallPerformance.RestartTiming("TotalEntityRenderTime"); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, samplerState, null, null); { EntitiesOnScreen = Collider.GetIntersectingEntites(new FRectangle(viewPortInfo.pxViewPortBounds)); // DRAW EACH ENTITIY THAT IS WITHIN THE SCREENS VIEWPORT foreach (Entity entity in EntitiesOnScreen) { EntityRenderPerformance.RestartTiming(entity.Name); if (!entity.Visible) continue; entity.IsOnScreen = true; // Determine the absolute position on the screen for entity position and the bounding box. Vector2 pxAbsEntityPos = new Vector2( entity.Pos.X * viewPortInfo.ActualZoom - globalDispX, entity.Pos.Y * viewPortInfo.ActualZoom - globalDispY ); FRectangle pxAbsBoundingBox = new FRectangle( entity.CurrentBoundingBox.X * viewPortInfo.ActualZoom - globalDispX, entity.CurrentBoundingBox.Y * viewPortInfo.ActualZoom - globalDispY, entity.CurrentBoundingBox.Width * viewPortInfo.ActualZoom, entity.CurrentBoundingBox.Height * viewPortInfo.ActualZoom ); // DRAW ENTITY BOUNDING BOXES IF ENABLED if (DrawingOptions.ShowBoundingBoxes) { spriteBatch.DrawRectangle(pxAbsBoundingBox.ToRectangle(), Color.Red, 0.0001f); SpriteBatchExtensions.DrawCross( spriteBatch, new Vector2( (int) Math.Ceiling(pxAbsEntityPos.X), (int) Math.Ceiling(pxAbsEntityPos.Y) ), 13, Color.Black, 0f); } // DRAW ENTITY DETAILS IF ENABLED (ENTITY DEBUG INFO) if (DrawingOptions.ShowEntityDebugInfo) { SpriteBatchExtensions.DrawMultiLineString( spriteBatch, spriteFont, entity.GetDebugInfo(), (int) entity.CurrentBoundingBox.Width * 2, 4, new Vector2( pxAbsBoundingBox.X + pxAbsBoundingBox.Width/2, pxAbsBoundingBox.Y + pxAbsBoundingBox.Height/2 ), Color.Purple); } // DRAW EVERY GAMEDRAWABLE INSTANCE CURRENTLY ACTIVE IN THE ENTITIES DRAWABLE SET. HashSet<GameDrawableInstance> drawableInstances = entity.Drawables.GetByState(entity.CurrentDrawableState); if (drawableInstances != null) { foreach (GameDrawableInstance drawable in drawableInstances) { if (!drawable.Visible) continue; // The relative position of the object should always be (X,Y) - (globalDispX, globalDispY). globalDispX and globalDispY // are based on viewPortInfo.TopLeftX and viewPortInfo.TopLeftY. viewPortInfo.TopLeftX and viewPortInfo.TopLeftY have // already been corrected in terms of the bounds of the WORLD map coordinates. This allows for panning at the edges. Rectangle pxCurrentFrame = drawable.GetSourceRectangle(LastUpdateTime); int pxObjectWidth = (int)Math.Ceiling(pxCurrentFrame.Width * entity.ScaleX * viewPortInfo.ActualZoom); int pxObjectHeight = (int)Math.Ceiling(pxCurrentFrame.Height * entity.ScaleY * viewPortInfo.ActualZoom); // Draw the Object based on the current Frame dimensions and the specified Object Width Height values. Rectangle objectDestRect = new Rectangle( (int)Math.Ceiling(pxAbsEntityPos.X) + (int)Math.Ceiling(drawable.Offset.X * viewPortInfo.ActualZoom), (int)Math.Ceiling(pxAbsEntityPos.Y) + (int)Math.Ceiling(drawable.Offset.Y * viewPortInfo.ActualZoom), pxObjectWidth, pxObjectHeight ); Vector2 drawableOrigin = new Vector2( (float)Math.Ceiling(drawable.Drawable.Origin.X * pxCurrentFrame.Width), (float)Math.Ceiling(drawable.Drawable.Origin.Y * pxCurrentFrame.Height) ); Color drawableColor = new Color() { R = drawable.Color.R, G = drawable.Color.G, B = drawable.Color.B, A = (byte)(drawable.Color.A * entity.Opacity) }; // Layer depth should depend how far down the object is on the map (Relative to Y). // Important to also take into account the animation layers for the entity. float layerDepth = Math.Min(0.99f, 1 / (entity.Pos.Y + ((float)drawable.Layer / Map.pxHeight))); // FINALLY ... DRAW spriteBatch.Draw( drawable.GetSourceTexture(LastUpdateTime), objectDestRect, pxCurrentFrame, drawableColor, drawable.Rotation, drawableOrigin, drawable.SpriteEffects, layerDepth); // DRAW BOUNDING BOXES OF EACH INDIVIDUAL DRAWABLE COMPONENT if (DrawingOptions.ShowDrawableComponents) { Rectangle drawableComponentRect = new Rectangle( (int)Math.Floor(objectDestRect.X - objectDestRect.Width * drawable.Drawable.Origin.X), (int)Math.Floor(objectDestRect.Y - objectDestRect.Height * drawable.Drawable.Origin.Y), objectDestRect.Width, objectDestRect.Height); SpriteBatchExtensions.DrawRectangle( spriteBatch, drawableComponentRect, Color.Blue, 0); } } } EntityRenderPerformance.StopTiming(entity.Name); } } spriteBatch.End(); OverallPerformance.StopTiming("TotalEntityRenderTime"); // APPLY GAME SHADERS TO THE RESULTANT IMAGE OverallPerformance.RestartTiming("TotalPostGameShaderRenderTime"); foreach(PostGameShader postGameShader in GameShaders) { if (postGameShader.Enabled) { postGameShader.ApplyShader(spriteBatch, viewPortInfo, LastUpdateTime, _inputBuffer, _outputBuffer); // Swap buffers after each render. _dummyBuffer = _inputBuffer; _inputBuffer = _outputBuffer; _outputBuffer = _dummyBuffer; } } OverallPerformance.StopTiming("TotalPostGameShaderRenderTime"); // DRAW COLLIDER DEBUG INFORMATION IF ENABLED if (DrawingOptions.ShowColliderDebugInfo) { spriteBatch.Begin(); Collider.DrawDebugInfo(viewPortInfo, spriteBatch, destRectangle, spriteFont, globalDispX, globalDispY); spriteBatch.End(); } // DRAW THE VIEWPORT TO THE STANDARD SCREEN GraphicsDevice.SetRenderTarget(null); spriteBatch.Begin(); { spriteBatch.Draw(_inputBuffer, destRectangle, color); } spriteBatch.End(); return viewPortInfo; }
internal void GetEntities(FRectangle? pxRegion, ref List<Entity> result) { if (Entities.Count > 0) { foreach (Entity entity in Entities) if (!result.Contains(entity)) // TODO: This needs to be removed. It is slow and should not be here result.Add(entity); } else { if (IsLeafNode) return; if (pxRegion == null || ChildNode1.Intersects(pxRegion.Value)) ChildNode1.GetEntities(pxRegion, ref result); if (pxRegion == null || ChildNode2.Intersects(pxRegion.Value)) ChildNode2.GetEntities(pxRegion, ref result); if (pxRegion == null || ChildNode3.Intersects(pxRegion.Value)) ChildNode3.GetEntities(pxRegion, ref result); if (pxRegion == null || ChildNode4.Intersects(pxRegion.Value)) ChildNode4.GetEntities(pxRegion, ref result); } }
public bool Contains(FRectangle frectangle) { return !(frectangle.Right < this.Left || frectangle.Right > this.Right || frectangle.Left < this.Left || frectangle.Left > this.Right || frectangle.Top < this.Top || frectangle.Top > this.Bottom || frectangle.Bottom < this.Top || frectangle.Bottom > this.Bottom); }
public void Remove(Entity Entity, FRectangle? pxBoundingBox) { List<QuadTreeNode> associations = new List<QuadTreeNode>(); GetAssociatedNodes(Entity, pxBoundingBox, ref associations); foreach (QuadTreeNode node in associations) { node.Entities.Remove(Entity); node.Validate(this); } }
public bool Intersects(FRectangle pxBoundingBox) { return pxBounds.Intersects(pxBoundingBox); }
// If pxBoundingBox is null, it will search everywhere public void GetAssociatedNodes(Entity entity, FRectangle? pxBoundingBox, ref List<QuadTreeNode> result) { if (IsLeafNode) { if (Entities.Contains(entity)) result.Add(this); return; } if (pxBoundingBox == null || ChildNode1.Intersects(pxBoundingBox.Value)) ChildNode1.GetAssociatedNodes(entity, pxBoundingBox, ref result); if (pxBoundingBox == null || ChildNode2.Intersects(pxBoundingBox.Value)) ChildNode2.GetAssociatedNodes(entity, pxBoundingBox, ref result); if (pxBoundingBox == null || ChildNode3.Intersects(pxBoundingBox.Value)) ChildNode3.GetAssociatedNodes(entity, pxBoundingBox, ref result); if (pxBoundingBox == null || ChildNode4.Intersects(pxBoundingBox.Value)) ChildNode4.GetAssociatedNodes(entity, pxBoundingBox, ref result); }
public bool Contains(FRectangle pxBoundingBox) { return pxBounds.Contains(pxBoundingBox); }
public static bool IntersectsWith(Entity entity, string entityState, string entityGroup, FRectangle bounds, GameTime gameTime) { HashSet<DrawableInstance> entityInstances = entity.Drawables.GetByState(entityState); if (entityInstances == null) return false; foreach (DrawableInstance instance in entityInstances) { if (entityGroup == null || instance._associatedGroup == entityGroup) { float entitySourceWidth = instance.GetWidth(gameTime) * entity.ScaleX; float entitySourceHeight = instance.GetHeight(gameTime) * entity.ScaleY; FRectangle absBoundingBox = new FRectangle( entity.Pos.X - entitySourceWidth * instance.Drawable.Origin.X, entity.Pos.Y - entitySourceHeight * instance.Drawable.Origin.Y, entitySourceWidth, entitySourceHeight ); if (bounds.Intersects(absBoundingBox)) return true; } } // No intersection tests passed. return false; }