Sorry this is so long, I wanted to proved enough code as it's hard to explain on it's own
I'm setting up a small test program to make a 2D visual gauge. The structure of the project is that I have a class; 'DirectX11Renderer' which initialises all the SharpDX objects to set up the factories, devices, back buffers, rendertargets, swapchains and layers.
This class then contains drawing methods for drawing basic shapes, filled shapes and text, they can likely be ignored, however the idea of the program is to be able to render the static content of the gauge on one layer (the background, stuff that doesn't really move) and the dynamic content on another layer, so each drawing method has an enum as an argument which specifies the layer to draw on, and then that is passed into this method, which starts drawing on the render target (if not already), pops the last pushed layer (if there was one) and pushes the layer that is required to be drawn on:
private void StartDrawingAndSwitchLayer(TargetLayer layer, bool draw = true)
{
if (draw && !isDrawing)
{
d2dRenderTarget.BeginDraw();
isDrawing = true;
}
switch (layer)
{
case TargetLayer.Static:
if (isDynamicLayerPushed)
{
d2dRenderTarget.PopLayer();
isDynamicLayerPushed = false;
}
if (!isStaticLayerPushed)
{
d2dRenderTarget.PushLayer(ref layerParams, staticLayer);
isStaticLayerPushed = true;
}
break;
case TargetLayer.Dynamic:
if (isStaticLayerPushed)
{
d2dRenderTarget.PopLayer();
isStaticLayerPushed = false;
}
if (!isDynamicLayerPushed)
{
d2dRenderTarget.PushLayer(ref layerParams, dynamicLayer);
isDynamicLayerPushed = true;
}
break;
case TargetLayer.None:
if (isStaticLayerPushed)
{
d2dRenderTarget.PopLayer();
isStaticLayerPushed = false;
}
if (isDynamicLayerPushed)
{
d2dRenderTarget.PopLayer();
isDynamicLayerPushed = false;
}
break;
}
if (!draw && isDrawing)
{
d2dRenderTarget.EndDraw();
isDrawing = false;
}
}
So in a class that uses an instance of this DirectX11Renderer class, it starts by setting the background colour without pushing a layer, rendering the static content by clearing the layer then calling several of the draw methods using the static layer as an argument for the enum to push the static layer, then rendering the dynamic content every time a value changes, doing the same thing by using the enum as an argument to pop the static layer and push the dynamic layer, then drawing on it. Finally at the end of the dynamic drawing method I present the frame by using the enum argument of TargetLayer.None to pop all layers, and then present the frame.
What I'm finding is that every time I change the value, (rendering the dynamic content on it's layer, popping the layer then presenting) the screen alternates between showing the content of the dynamic layer on a black background, and all the static and dynamic content as I would want to see it if this were working 100%.
As a minimum working example, here is the code for a form which contains the gauge control:
using System.Windows.Forms;
using System;
namespace SharpDXGauge
{
public partial class Form1 : Form
{
Gauge control;
public Form1()
{
InitializeComponent();
control = new Gauge(100, 0);
control.Dock = DockStyle.Left;
this.Controls.Add(control);
}
private void numericUpDown1_ValueChanged(object sender, EventArgs e)
{
control.IndicatorValue = (float)numericUpDown1.Value;
}
}
}
Here is a minimal example of the gauge class (I've removed most of the rendering):
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Collections.Generic;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.DirectWrite;
using DXGI = SharpDX.DXGI; // Direct X Graphics Infrastructure
using D3D = SharpDX.Direct3D;
using D2D1 = SharpDX.Direct2D1;
using D3D11 = SharpDX.Direct3D11;
using DWrite = SharpDX.DirectWrite;
namespace SharpDXGauge
{
public partial class Gauge : UserControl
{
#region Members
private DirectX11Renderer renderer;
private float max;
private float min;
private float indicatorValue;
private Point topLeft;
private Point bottomRight;
private float height;
private float width;
Point valueTextTopLeft;
Point valueLabelOutlineTopLeft;
Point valueLabelOutlineBottomRight;
#endregion
public Gauge(float max, float min)
{
InitializeComponent();
renderer = new DirectX11Renderer(this);
this.max = max;
this.min = min;
this.indicatorValue = min;
renderer.SetBackgroundColor(renderer.SystemDrawingColorToSharpDXColor(this.BackColor));
DrawStaticContent();
DrawDynamicContent();
}
#region Drawing
private void CalculateSizes()
{
// Static
topLeft = new Point() { x = 25, y = 10 };
bottomRight = new Point() { x = topLeft.x + 20, y = this.Height - 50 };
height = bottomRight.y - topLeft.y;
width = bottomRight.x - topLeft.x;
// Dynamic
string valueText = indicatorValue.ToString();
Size2F valueTextSize = renderer.GetTextSize(valueText, 9.75f);
valueTextTopLeft = new Point() { x = bottomRight.x + 15, y = bottomRight.y - height * indicatorValue / (max - min) - (valueTextSize.Height / 2) };
}
private void DrawStaticContent()
{
CalculateSizes();
renderer.ClearLayer(TargetLayer.Static, Color.Transparent);
renderer.DrawRectangle(TargetLayer.Static, topLeft, bottomRight, Color.Black);
DrawDynamicContent();
renderer.PresentFrame();
}
private void DrawDynamicContent()
{
renderer.ClearLayer(TargetLayer.Dynamic, Color.Transparent);
CalculateSizes();
renderer.DrawText(TargetLayer.Dynamic, indicatorValue.ToString(), Color.White, valueTextTopLeft, 9.75f);
renderer.DrawRoundedRectangle(TargetLayer.Dynamic, valueLabelOutlineTopLeft, valueLabelOutlineBottomRight, 20, Color.Black);
renderer.PresentFrame();
}
#endregion
#region Properties
public float IndicatorValue
{
get
{
return indicatorValue;
}
set
{
if (indicatorValue != value)
{
indicatorValue = value;
DrawDynamicContent();
}
}
}
#endregion
#region Disposal
public new void Dispose()
{
renderer.Dispose();
}
#endregion
}
}
And my DirectX11Renderer class, I've removed all the unnecessary drawing methods etc:
using System;
using System.Windows.Forms;
using System.Diagnostics;
using System.Collections.Generic;
using SharpDX;
using SharpDX.Direct2D1;
using SharpDX.Direct3D;
using SharpDX.Direct3D11;
using SharpDX.DXGI;
using SharpDX.DirectWrite;
using DXGI = SharpDX.DXGI; // Direct X Graphics Infrastructure
using D3D = SharpDX.Direct3D;
using D2D1 = SharpDX.Direct2D1;
using D3D11 = SharpDX.Direct3D11;
using DWrite = SharpDX.DirectWrite;
using DMaths = SharpDX.Mathematics.Interop;
namespace SharpDXGauge
{
public enum TargetLayer
{
Static,
Dynamic,
None
}
public struct Point
{
public float x;
public float y;
}
public partial class DirectX11Renderer : IDisposable
{
private Surface backBuffer;
private D3D11.Device d3dDevice;
private D3D11.Device1 d3dDevice1;
private DXGI.Device2 dxgiDevice2;
private D2D1.Device d2dDevice;
private SwapChain1 swapChain1;
private D2D1.Factory d2dFactory;
private DXGI.Factory2 dxgiFactory2;
private Adapter dxgiAdapter;
private RenderTarget d2dRenderTarget;
private DWrite.Factory dWriteFactory;
private Layer staticLayer;
private Layer dynamicLayer;
SolidColorBrush solidColorBrush;
//RadialGradientBrush radialGradientBrush;
//LinearGradientBrush linearGradientBrush;
private Color lastBackColour;
private bool isDrawing;
private LayerParameters layerParams;
private bool isStaticLayerPushed;
private bool isDynamicLayerPushed;
List<double> renderTimesMsec = new List<double>();
public DirectX11Renderer(Control surfaceToDrawOn)
{
// There seems to be endless ways to set up the different objects required for rendering
// This is the most efficient I've found that get's everything you need for 2d rendering
SwapChainDescription1 description = new SwapChainDescription1()
{
Width = surfaceToDrawOn.Width,
Height = surfaceToDrawOn.Height,
Format = Format.R8G8B8A8_UNorm,
Stereo = false,
SampleDescription = new SampleDescription(1, 0),
Usage = Usage.BackBuffer | Usage.RenderTargetOutput,
BufferCount = 2,
Scaling = Scaling.None,
SwapEffect = SwapEffect.FlipSequential,
};
d3dDevice = new D3D11.Device(DriverType.Hardware, DeviceCreationFlags.BgraSupport);
d3dDevice1 = d3dDevice.QueryInterface<D3D11.Device1>();
dxgiDevice2 = d3dDevice1.QueryInterface<DXGI.Device2>();
d2dDevice = new D2D1.Device(dxgiDevice2);
dxgiAdapter = dxgiDevice2.Adapter;
d2dFactory = new D2D1.Factory(D2D1.FactoryType.SingleThreaded);
dxgiFactory2 = dxgiAdapter.GetParent<DXGI.Factory2>();
swapChain1 = new SwapChain1(dxgiFactory2, d3dDevice1, surfaceToDrawOn.Handle, ref description);
backBuffer = swapChain1.GetBackBuffer<Surface>(0);
d2dRenderTarget = new RenderTarget(d2dFactory, backBuffer,
new RenderTargetProperties(new PixelFormat(Format.R8G8B8A8_UNorm, D2D1.AlphaMode.Premultiplied)));
staticLayer = new Layer(d2dRenderTarget);
dynamicLayer = new Layer(d2dRenderTarget);
dWriteFactory = new DWrite.Factory(DWrite.FactoryType.Shared);
layerParams = new LayerParameters()
{
ContentBounds = RectangleF.Infinite,
Opacity = 1
};
solidColorBrush = new SolidColorBrush(d2dRenderTarget, Color.White);
}
public void PresentFrame()
{
StartDrawingAndSwitchLayer(TargetLayer.None, false);
swapChain1.Present(0, PresentFlags.None);
}
public void SetBackgroundColor(Color colour)
{
StartDrawingAndSwitchLayer(TargetLayer.None);
lastBackColour = colour;
d2dRenderTarget.Clear(colour);
}
public void ClearLayer(TargetLayer layer, Color fillColorAfterClear)
{
StartDrawingAndSwitchLayer(layer);
d2dRenderTarget.Clear(fillColorAfterClear);
}
public void DrawRectangle(TargetLayer layer, Point topLeft, Point bottomRight, Color colour, float strokeWidth = 1)
{
StartDrawingAndSwitchLayer(layer);
solidColorBrush.Color = colour;
d2dRenderTarget.DrawRectangle(new DMaths.RawRectangleF(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y), solidColorBrush, strokeWidth);
}
public void DrawRoundedRectangle(TargetLayer layer, Point topLeft, Point bottomRight, float roundingRadius, Color colour, float strokeWidth = 1)
{
StartDrawingAndSwitchLayer(layer);
solidColorBrush.Color = colour;
RoundedRectangle roundedRect = new RoundedRectangle();
roundedRect.Rect = new DMaths.RawRectangleF(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y);
roundedRect.RadiusX = roundingRadius;
roundedRect.RadiusY = roundingRadius;
d2dRenderTarget.DrawRoundedRectangle(roundedRect, solidColorBrush, strokeWidth);
}
public void DrawText(TargetLayer layer, string text, Color colour, Point topLeft, float fontSize, FontWeight weight = FontWeight.Normal, FontStyle style = FontStyle.Normal, string font = "Microsoft Sans Serif")
{
StartDrawingAndSwitchLayer(layer);
solidColorBrush.Color = colour;
TextFormat format = new TextFormat(dWriteFactory, font, weight, style, FontStretch.Normal, fontSize);
Size2F textSize = GetTextSize(text, fontSize, 100, 100);
Point bottomRight = new Point() { x = topLeft.x + (int)Math.Ceiling(textSize.Width), y = topLeft.y + (int)Math.Ceiling(textSize.Height) };
d2dRenderTarget.DrawText(text, format,
new DMaths.RawRectangleF(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y),
solidColorBrush);
}
public Size2F GetTextSize(string text, float fontSize, float maxWidth = 100000, float maxHeight = 100000, FontWeight weight = FontWeight.Normal, FontStyle style = FontStyle.Normal, string font = "Microsoft Sans Serif")
{
TextFormat format = new TextFormat(dWriteFactory, font, weight, style, FontStretch.Normal, fontSize);
TextLayout layout = new TextLayout(dWriteFactory, text, format, maxWidth, maxHeight);
return new Size2F(layout.Metrics.Width, layout.Metrics.Height);
}
public Color SystemDrawingColorToSharpDXColor(System.Drawing.Color colour)
{
Color toRtn = new Color(new byte[4] { colour.R, colour.G, colour.B, colour.A, });
return toRtn;
}
private void StartDrawingAndSwitchLayer(TargetLayer layer, bool draw = true)
{
if (draw && !isDrawing)
{
d2dRenderTarget.BeginDraw();
isDrawing = true;
}
switch (layer)
{
case TargetLayer.Static:
if (isDynamicLayerPushed)
{
d2dRenderTarget.PopLayer();
isDynamicLayerPushed = false;
}
if (!isStaticLayerPushed)
{
d2dRenderTarget.PushLayer(ref layerParams, staticLayer);
isStaticLayerPushed = true;
}
break;
case TargetLayer.Dynamic:
if (isStaticLayerPushed)
{
d2dRenderTarget.PopLayer();
isStaticLayerPushed = false;
}
if (!isDynamicLayerPushed)
{
d2dRenderTarget.PushLayer(ref layerParams, dynamicLayer);
isDynamicLayerPushed = true;
}
break;
case TargetLayer.None:
if (isStaticLayerPushed)
{
d2dRenderTarget.PopLayer();
isStaticLayerPushed = false;
}
if (isDynamicLayerPushed)
{
d2dRenderTarget.PopLayer();
isDynamicLayerPushed = false;
}
break;
}
if (!draw && isDrawing)
{
d2dRenderTarget.EndDraw();
isDrawing = false;
}
}
public void Dispose()
{
// Release all resources
backBuffer.Dispose();
d3dDevice.Dispose();
d3dDevice1.Dispose();
dxgiDevice2.Dispose();
d2dDevice.Dispose();
swapChain1.Dispose();
d2dFactory.Dispose();
dxgiFactory2.Dispose();
dxgiAdapter.Dispose();
d2dRenderTarget.Dispose();
staticLayer.Dispose();
dynamicLayer.Dispose();
}
}
}
Putting this together gives a minimal example of what I'm seeing, I've removed code handling form resizing so with this, the bottom half appears black to begin with, but using a numerical updown on the form to update the IndicatorValue property creates the alternating all black/ what I want to see frames.
I've been trying to ebug this for a while, I'm beginning to wonder if it's to do with how I've set up my swap chain? At first I thought it must be how I'm handing my layers, and perhaps somehow every other change and dynamic content render it's not popping the layer and only showing the content of the dynamic layer? But stepping through each update has it pushing and popping the layers correctly as far as I can see, and generally not doing it right throws an exception.
Sorry this is so long,
Joe.
I am building an AR application that spawns a 3d model when user taps the screen on to the ground plane.
But the issue I am facing is that, I have placed a button at the bottom of the screen to take a screenshot. On clicking the button,it does take screenshots,but it spawns an image below the button also.
How can I avoid this ? Can I use raycast to detect a button,and not spawn the object there ?
Below is the code I use to place objects
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;
using UnityEngine.Experimental.XR;
using System;
public class ArTapToPlace : MonoBehaviour
{
public GameObject objectToPlace;
public GameObject placementIndicator;
private ARSessionOrigin arOrigin;
private Pose placementPose;
private bool placementPoseIsValid = false;
void Start()
{
arOrigin = FindObjectOfType<ARSessionOrigin>();
}
void Update()
{
UpdatePlacementPose();
UpdatePlacementIndicator();
if (placementPoseIsValid && Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
PlaceObject();
}
}
private void PlaceObject()
{
Instantiate(objectToPlace, placementPose.position, placementPose.rotation);
}
private void UpdatePlacementIndicator()
{
print(placementIndicator);
if (placementPoseIsValid)
{
placementIndicator.SetActive(true);
placementIndicator.transform.SetPositionAndRotation(placementPose.position, placementPose.rotation);
}
else
{
placementIndicator.SetActive(false);
}
}
private void UpdatePlacementPose()
{
var screenCenter = Camera.current.ViewportToScreenPoint(new Vector3(0.5f, 0.5f));
var hits = new List<ARRaycastHit>();
arOrigin.GetComponent<ARRaycastManager>().Raycast(screenCenter, hits, UnityEngine.XR.ARSubsystems.TrackableType.Planes);
print(hits.Count);
print(hits);
placementPoseIsValid = hits.Count > 0;
if (placementPoseIsValid)
{
placementPose = hits[0].pose;
var cameraForward = Camera.current.transform.forward;
var cameraBearing = new Vector3(cameraForward.x, 0, cameraForward.z).normalized;
placementPose.rotation = Quaternion.LookRotation(cameraBearing);
}
}
}
Depending on how your UI is setup you can probably use EventSystems.EventSystem.IsPointerOverGameObject for checking if the pointer is over a UI element and skip the placing in this case
if (placementPoseIsValid && Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
{
if(EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))) return;
PlaceObject();
}
I have implemented a rubber band by adopting the following code:
https://support.microsoft.com/en-gb/kb/314945
This is my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Reflection;
namespace Test
{
public partial class TestX: UserControl
{
private Boolean m_bLeftButton { get; set; }
private Boolean m_bMiddleButton { get; set; }
private Boolean m_bZoomWindow { get; set; }
Point m_ptOriginal = new Point();
Point m_ptLast = new Point();
public TestX()
{
m_bZoomWindow = false;
m_bLeftButton = false;
m_bMiddleButton = false;
}
// Called when the left mouse button is pressed.
public void MyMouseDown(Object sender, MouseEventArgs e)
{
if(m_bZoomWindow && e.Button == MouseButtons.Left)
{
// Make a note that we "have the mouse".
m_bLeftButton = true;
// Store the "starting point" for this rubber-band rectangle.
m_ptOriginal.X = e.X;
m_ptOriginal.Y = e.Y;
// Special value lets us know that no previous
// rectangle needs to be erased.
m_ptLast.X = -1;
m_ptLast.Y = -1;
}
}
// Convert and normalize the points and draw the reversible frame.
private void MyDrawReversibleRectangle(Point p1, Point p2)
{
Rectangle rc = new Rectangle();
// Convert the points to screen coordinates.
p1 = PointToScreen(p1);
p2 = PointToScreen(p2);
// Normalize the rectangle.
if (p1.X < p2.X)
{
rc.X = p1.X;
rc.Width = p2.X - p1.X;
}
else
{
rc.X = p2.X;
rc.Width = p1.X - p2.X;
}
if (p1.Y < p2.Y)
{
rc.Y = p1.Y;
rc.Height = p2.Y - p1.Y;
}
else
{
rc.Y = p2.Y;
rc.Height = p1.Y - p2.Y;
}
// Draw the reversible frame.
ControlPaint.DrawReversibleFrame(rc,
Color.WhiteSmoke, FrameStyle.Thick);
}
// Called when the left mouse button is released.
public void MyMouseUp(Object sender, MouseEventArgs e)
{
if(m_bZoomWindow && e.Button == MouseButtons.Left)
{
// Set internal flag to know we no longer "have the mouse".
m_bZoomWindow = false;
m_bLeftButton = false;
// If we have drawn previously, draw again in that spot
// to remove the lines.
if (m_ptLast.X != -1)
{
Point ptCurrent = new Point(e.X, e.Y);
MyDrawReversibleRectangle(m_ptOriginal, m_ptLast);
// Do zoom now ...
}
// Set flags to know that there is no "previous" line to reverse.
m_ptLast.X = -1;
m_ptLast.Y = -1;
m_ptOriginal.X = -1;
m_ptOriginal.Y = -1;
}
}
// Called when the mouse is moved.
public void MyMouseMove(Object sender, MouseEventArgs e)
{
Point ptCurrent = new Point(e.X, e.Y);
if(m_bLeftButton)
{
// If we "have the mouse", then we draw our lines.
if (m_bZoomWindow)
{
// If we have drawn previously, draw again in
// that spot to remove the lines.
if (m_ptLast.X != -1)
{
MyDrawReversibleRectangle(m_ptOriginal, m_ptLast);
}
// Update last point.
if(ptCurrent != m_ptLast)
{
m_ptLast = ptCurrent;
// Draw new lines.
MyDrawReversibleRectangle(m_ptOriginal, ptCurrent);
}
}
}
}
// Set up delegates for mouse events.
protected override void OnLoad(System.EventArgs e)
{
MouseDown += new MouseEventHandler(MyMouseDown);
MouseUp += new MouseEventHandler(MyMouseUp);
MouseMove += new MouseEventHandler(MyMouseMove);
MouseWheel += new MouseEventHandler(MyMouseWheel);
m_bZoomWindow = false;
}
}
}
It itself it works, but the rectangle flashes. Other programs, like CAD packages, have zero flickering when drawing a rectangle.
My form is set to use DoubleBuffering so I thought it would be OK. Has anyone else encountered this issue?
Update: I thought I would go back to the beginning and do a test winforms project with a table layout panel and a embedded user control. I set the user control to work like the answer I was provided. The only difference was that I set the user control constructor like this:
public MyUserControl()
{
InitializeComponent();
_selectionPen = new Pen(Color.Black, 3.0f);
SetStyle(ControlStyles.OptimizedDoubleBuffer |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint, true);
BackColor = Color.Transparent;
Dock = DockStyle.Fill;
Margin = new Padding(1);
}
Notice the additional control styles AllPaintingInWmPaint and UserPaint? It seems that I needed these in addition to the OptimizedDoubleBuffer style. I also set the form as double buffered.
When I make those adjustments I can draw a flicker free rubber band. Hoorah! But when I add back in the embedded class for rendering a DWG in the user control, I get the conflict of one over imposing the other.
So that is where I am at and I will wait to see what I can glean from the vendors of the DWG viewer class.
To demonstrate using the Paint event, from my comment to the OP.
Smoothest box draw I was able to get was by doing it in Paint and setting ControlStyles.OptimizeDoubleBuffer on the control. Of course, it depends on the intended bounds of your box -- this will not exceed the bounds of the control itself (i.e. will not draw onto the form or desktop):
using System.Drawing;
using System.Windows.Forms;
namespace WinformsScratch.RubberBand
{
public class TestY : Control
{
private Point? _selectionStart;
private Point? _selectionEnd;
private readonly Pen _selectionPen;
public TestY()
{
_selectionPen = new Pen(Color.Black, 3.0f);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
MouseDown += (s, e) => {
if (e.Button == MouseButtons.Left)
_selectionStart = _selectionEnd = e.Location;
};
MouseUp += (s, e) => {
if (e.Button == MouseButtons.Left)
{
_selectionStart = _selectionEnd = null;
Invalidate(false);
}
};
MouseMove += (s, e) => {
if (_selectionStart.HasValue &&
_selectionEnd.HasValue &&
_selectionEnd.Value != e.Location)
{
_selectionEnd = e.Location;
Invalidate(false);
}
};
Paint += (s, e) => {
if (_selectionStart.HasValue && _selectionEnd.HasValue)
e.Graphics.DrawRectangle(_selectionPen, GetSelectionRectangle());
};
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
if (_selectionPen != null) _selectionPen.Dispose();
}
base.Dispose(disposing);
}
private Rectangle GetSelectionRectangle()
{
Rectangle rc = new Rectangle();
if (_selectionStart.HasValue && _selectionEnd.HasValue)
{
// Normalize the rectangle.
if (_selectionStart.Value.X < _selectionEnd.Value.X)
{
rc.X = _selectionStart.Value.X;
rc.Width = _selectionEnd.Value.X - _selectionStart.Value.X;
}
else
{
rc.X = _selectionEnd.Value.X;
rc.Width = _selectionStart.Value.X - _selectionEnd.Value.X;
}
if (_selectionStart.Value.Y < _selectionEnd.Value.Y)
{
rc.Y = _selectionStart.Value.Y;
rc.Height = _selectionEnd.Value.Y - _selectionStart.Value.Y;
}
else
{
rc.Y = _selectionEnd.Value.Y;
rc.Height = _selectionStart.Value.Y - _selectionEnd.Value.Y;
}
}
return rc;
}
}
}
I'm rather new at monogame and I've ran into yet another issue that I can't seem to figure out. After a few other issues (that have been resolved by you amazing people!) I've ran into another!
I can't seem the figure out why my mobs won't spawn, everything else spawns and works as it should but the enemies will not spawn. I'm relatively new at this so I haven't really tried anything but looking online at tutorials to see if ive done it correctly but I cannot see what I have done wrong.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Content;
namespace Roid_shoooota
{
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//player and BG stuff
PlayerShip Player = new PlayerShip();
Background bG = new Background();
//asteroid stuff
List<Roids> asteroidList = new List<Roids>();
Random random = new Random();
//mob stuff
public int enemyBulletDamage;
List<Mobs> mobsList = new List<Mobs>();
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.IsFullScreen = false;
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 950;
this.Window.Title = "Roid Shoota";
enemyBulletDamage = 10;
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
Player.LoadContent(Content);
bG.LoadContent(Content);
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
//updating mobs and checking for collisions
foreach (Mobs M in mobsList)
{
if (M.hitBox.Intersects(Player.boundingBox))
{
Player.health -= 40;
M.isVisible = false;
}
//enemy bullet collision with player
for (int i = 0; i < M.bulletList.Count; i++)
{
if (Player.boundingBox.Intersects(M.bulletList[i].hitBox))
{
Player.health -= enemyBulletDamage;
M.bulletList[i].isVisible = false;
}
}
for (int i = 0; i < Player.bulletList.Count; i++)
{
if (Player.bulletList[i].hitBox.Intersects(M.hitBox))
{
Player.bulletList[i].isVisible = false;
M.isVisible = false;
}
}
M.update(gameTime);
}
//updating the asteroid list, check for collisions
foreach (Roids R in asteroidList)
{
//if a roid hits the player get oot me game
if (R.hitBox.Intersects(Player.boundingBox))
{
Player.health -= 20;
R.isVisible = false;
}
for (int i = 0; i < Player.bulletList.Count; i++)
{
if (R.hitBox.Intersects(Player.bulletList[i].hitBox))
{
R.isVisible = false;
Player.bulletList.ElementAt(i).isVisible = false;
}
}
R.update(gameTime);
}
Player.update(gameTime);
bG.update(gameTime);
loadAsteroids();
loadMobs();
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
bG.Draw(spriteBatch);
Player.draw(spriteBatch);
foreach (Roids R in asteroidList)
{
R.Draw(spriteBatch);
}
foreach (Mobs M in mobsList)
{
M.Draw(spriteBatch);
}
spriteBatch.End();
base.Draw(gameTime);
}
//random roid spawns
public void loadAsteroids()
{
int randY = random.Next(-600, -50);
int randX = random.Next(0, 750);
//create more if less than 5
if(asteroidList.Count() < 40)
{
asteroidList.Add(new Roids(Content.Load<Texture2D>("asteroid"), new Vector2(randX, randY)));
}
//if destoryed or off screen remove it from da list
for (int i = 0; i < asteroidList.Count; i++)
{
if (!asteroidList[i].isVisible)
{
asteroidList.RemoveAt(i);
i--;
}
}
}
//load mobs
public void loadMobs()
{
int randY = random.Next(-600, -50);
int randX = random.Next(0, 750);
//create more if less than 3
if (mobsList.Count() < 3)
{
mobsList.Add(new Mobs(Content.Load<Texture2D>("enemyship"), new Vector2(randX, randY), Content.Load<Texture2D>("EnemyBullet")));
}
//if destoryed or off screen remove it from da list
for (int i = 0; i < mobsList.Count; i++)
{
if (!mobsList[i].isVisible)
{
mobsList.RemoveAt(i);
i--;
}
}
}
}
}
I was looking for a way to block other object from the same class to be dragged, when I begin dragging one and hover another it id dragged as well...
Here is my main item definition,
class DrawableObject
{
public Rectangle RectObject
public void Update(GameTime gameTime, MouseState ms)
{
if ((ButtonState.Pressed == Mouse.GetState().LeftButton))
if (RectObject.Intersects(new Rectangle(ms.X, ms.Y, 0, 0)))
Dragged = true;
else
Dragged = false;
if (Dragged)
RectObject = new Rectangle(ms.X - RectObject.Width / 2, ms.Y - RectObject.Height / 2, RectObject.Width, RectObject.Height);
}
}
The issue will be more tight as I will be creating more than 50 instance of this class.
You could keep a "global" property with the current object being dragged.
Then before accepting dragging each object will check if another is not yet dragged:
public void Update(GameTime gameTime, MouseState ms)
{
if (currentObject != null && this != currentObject) return;
if ((ButtonState.Pressed == Mouse.GetState().LeftButton))
{
if (RectObject.Intersects(new Rectangle(ms.X, ms.Y, 0, 0)))
{
Dragged = true;
currentObject = this;
}
}
else
...
}