I am developing a game in MonoGame and decided to create my own button class. In order to determine if these buttons were clicked, I determined if they were in the bounds of the button, and if the left mouse button was down. This has lead to two problems.
The mouse click doesn't need to start inside the button, and it registers immediately rather than after the mouse button is released (Inside the bounds of the button.)
When multiple buttons on different screens are in the same area, it results in clicking them both as the mouse button did not release fast enough.
How can I make the clicking behave more like the WinForm buttons?
To make mouseclicking work effectively you should do the following :
First thing we do is create a MouseInput class, this should track stuff like, mouseState, lastMouseState, MouseX, MouseY. The mouseState and lastMouseState work together to handle 1 single click. But for now you can just add this class to your project :
class MouseInput
{
private static MouseState mouseState;
private static MouseState lastMouseState;
public static MouseState MouseState
{
get { return mouseState; }
set { mouseState = value; }
}
public static MouseState LastMouseState
{
get
{
return lastMouseState;
}
set
{
lastMouseState = value;
}
}
public MouseInput()
{
}
public static int getMouseX()
{
return Mouse.GetState().X;
}
public static int getMouseY()
{
return Mouse.GetState().Y;
}
}
After you've done that you want to start loooking for a mouseclick everytime your update method gets executed :
MouseInput.LastMouseState = MouseInput.MouseState;
// Get the mouse state relevant for this frame
MouseInput.MouseState = Mouse.GetState();
After you've completed these 2 crucial steps you can continue and use your code anywhere you want like this :
if (MouseInput.LastMouseState.LeftButton == ButtonState.Released && MouseInput.MouseState.LeftButton == ButtonState.Pressed) {
//Execute code here
}
Ofcourse as the above if statement only checks if the player pressed the left mouse button, you'll still have to check if the mouse positions are inside the button.
If you have any questions, feel free to ask
You can use RoutedEvent that can invoke handlers on multiple listeners in an element tree, rather than just on the object that raised the event. you can read more about RoutedEvents because The concept of an attached event enables you to add a handler for a particular event to an arbitrary element rather than to an element that actually defines or inherits the event. When those events are not related to the element which, like in case of MouseDown, both raises and listens to the event.
So RoutedEvents might help you to solve your problem
Here is a sample code
public static readonly RoutedEvent SelectedEvent =
EventManager.RegisterRoutedEvent( "Selected", RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(MyCustomControl));
// .NET wrapper
public event RoutedEventHandler Selected
{
add { AddHandler(SelectedEvent, value); }
remove { RemoveHandler(SelectedEvent, value); }
}
// Raise the routed event "selected"
RaiseEvent(new RoutedEventArgs(MyCustomControl.SelectedEvent));
UI for XNA 4.0 like WinForms, written from scratch.
Create form and, button to it, call Form.Update() from Game.Update() method.
In Game.Draw() first call Form.Refresh(), then Form.Draw().
For processing mouse and keyboard input used managers from GameHelper.Input library.
Related
I have a list that loads its content dynamically. The Elements of the List are buttons that are being transformed into gameobjects. For every object in the user list a new Element is created. Now I want to be able to delete the Element along with the button itself, when the button is pressed, where each Element has its own dynamically created delete button. I tried putting a box collider with an OnMouseDown Event, however onMouseDown is never called.
public void ShowLectures()
{
foreach (var course in selectedCourses)
{
AddMoreButton();
}
}
public void AddMoreButton()
{
GameObject button = (GameObject)Instantiate(prefabButton);
button.transform.SetParent(panel.transform, false);
button.layer = 5;
button.SetActive(true);
}
public void OnMouseDown()
{
Destroy(gameObject);
}
First of all OnMouseDown is not called on the created Button but in your case rather on the GameObject your script is attached to.
Then
Destroy(gameObject);
the same way would destroy the GameObject your script is attached to, not the instantiated button GameObjects.
Since you commented that you are using a UI.Button component rather add it to the onClick event as a callback when the Button is instantiated :
public void AddMoreButton()
{
GameObject button = (GameObject)Instantiate(prefabButton);
button.transform.SetParent(panel.transform, false);
button.layer = 5;
button.SetActive(true);
// get the Button on the button object or any child
var buttonInstance = button.GetComponentInChildren<Button>();
// add a callback to destroy the button gameObject
// () => {} is a lambda expression
buttonInstance.onClick.AddCallback(()=>{ Destroy(button); });
}
A little hint: In case the Button is already on the same GameObject anyway you should rather change the prefab type itself to Button like e.g.
public Button prefabButton;
and than simply use
public void AddMoreButton()
{
Button button = Instantiate(prefabButton, panel.transform);
button.gameObject.layer = 5;
button.gameObject.SetActive(true);
// add a callback to destroy the button gameObject
// () => {} is a lambda expression
button.onClick.AddListener(()=>{ Destroy(button.gameObject); });
}
Instead of creating a gameobject with an OnMouseDown, I'd probably create a Button instead and binding an event to it:
var closeButton = /* Dynamically create button */
closeButton.GetComponent<Button>().onClick.AddListener(HandleCloseClick);
void HandleCloseClick() {
Destroy(gameObject);
}
You can, however, do the architecture you've already implemented - you just need to implement IPointerClickHandler.
Here's an answer describing how to implement & use the interface.
Check the docs for instructions on how to use each event (Mouse Down, Up, Enter, Exit)
I need to create a set of Toggle at run-time and set a listener when one of those change. So this implies to know who changed.
I see how to do it statically in the UI but not programmatically and it's hard to find documentation on this subject.
The Toggle.onValueChanged event is used to subscribe to the Toggle event to detect when it is toggled. Toggle.toggle.isOn is used to check the status of the toggle. Use AddListener and delegate to help register the toggle so that you will get the name of the toggle when it is toggled:
public Toggle toggle1;
void OnEnable()
{
//Register Toggle Events
toggle1.onValueChanged.AddListener(delegate
{
ToggleCallValueChanged(toggle1);
});
}
private void ToggleCallValueChanged(Toggle toggle)
{
Debug.Log("Toggle: " + toggle + " is " + toggle.isOn);
}
void OnDisable()
{
//Un-Register Toggle Events
toggle1.onValueChanged.RemoveAllListeners();
}
If using multiple toggles, you can also re-use that one function but use the if statement to determine which one is toggled. You do this if the toggles are related. This is better than creating a new callback function for each toggle control.
Example of 3 toggles detected with one function:
public Toggle toggle1;
public Toggle toggle2;
public Toggle toggle3;
void OnEnable()
{
//Register Toggle Events
toggle1.onValueChanged.AddListener(delegate { ToggleCallBack(toggle1); });
toggle2.onValueChanged.AddListener(delegate { ToggleCallBack(toggle2); });
toggle3.onValueChanged.AddListener(delegate { ToggleCallBack(toggle3); });
}
private void ToggleCallBack(Toggle toggle)
{
if (toggle == toggle1)
{
//Your code for Toggle 1
Debug.Log("Toggled: " + toggle1.name);
}
if (toggle == toggle2)
{
//Your code for Toggle 2
Debug.Log("Toggled: " + toggle2.name);
}
if (toggle == toggle3)
{
//Your code for Toggle 3
Debug.Log("Toggled: " + toggle3.name);
}
}
void OnDisable()
{
//Un-Register Toggle Events
toggle1.onValueChanged.RemoveAllListeners();
toggle2.onValueChanged.RemoveAllListeners();
toggle3.onValueChanged.RemoveAllListeners();
}
You can use what is called an Observer pattern. That's many systems of events work.
Basically, each of your toggles created is able to send some event "I was toggled" (with giving some ID, name, value, etc.... to some object that has subscribed to it.
It also has a function "subscribe" or "attach", that allows any object to subscribe to it at runtime.
When you subscribe, you provide a callback function (which what could be called and event handler, if you have already heard the term).
That function is what will happen when the toggled is used : it will simply look at the list of object that has subscribe to it, and call the callback on each of them. That's the way with which the toggle will communicate the event.
Basically, in your case, you probably will have simple one controller which will subscribe to the toggle. This controller could be (but not necessarily) the class that created the toggles at the first place.
The whole pattern is useful because there is absolutely no need for the toggles to be aware of each other or of the controller in their code, (low coupling). And you never have to constantly check if something has changed, the "information" is properly sent only when relevant.
See Programmers's answer for a possible implementation.
I have a winforms control derived from Button, whose purpose is to display some dialog.
So, the control can be "activated" by tabbing or by a mouse click according to the application state. Forcing tab access is required under some circumstances, to ensure previous fields are filled.
Therefore, I need to "capture" mouse clicks on it, so to allow or disallow the mouse event according to the application state.
To trap the tab key, I override OnEnter() and it works ok.
I also overrided OnClick(), OnMouseDown() and OnMouseClick() at no avail: the program control is always silently passed to the OnEnter() method.
The debugger never stops in the OnClick() or OnMouse*() methods, so I cannot signal the event origin and prevent the core execution when needed.
Could someone help? Of course, I don´t like to trap the windows message queue; some previous experiences were not happy.
TIA
Using simple message pump filtering, as described here (https://www.daniweb.com/programming/software-development/threads/304777/how-to-temporarily-disabling-mouse-clicks-on-a-vb-net-form) with a class like this
// relevant usings...
using System.Security.Permissions;
...
using System.Windows.Forms;
namespace YourNameSpace
{
public class MouseFilter : IMessageFilter
{
protected static bool inEffect = false;
public static bool ActiveFiltering { set { inEffect = value; } }
const int LButtonDown = 0x201;
const int LButtonUp = 0x202;
const int LButtonDoubleClick = 0x203;
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
public bool PreFilterMessage( ref Message m )
{
if (!inEffect)
return false;
bool result = false;
switch (m.Msg) {
case LButtonDown:
case LButtonUp:
case LButtonDoubleClick:
result = true;
break;
}
return result;
}
}
}
The static property ActiveFiltering provides a way to enable/disable mouse clicks filtering as required.
Of course, the Program class must execute
Application.AddMessageFilter(new MouseFilter());
but, being inEffect = false, it don´t interfere normal general mouse operation.
My control disables the mouse when required, and takes care to left it enabled otherwise.
The solution is not perfect, but is what´s possible. The worst drawback is that while the control cancels the mouse clicks, no one can go to other control or the window self.
I got a Problem with creating and moving a point (an ellipse) in WPF MVVM.
Right now i have a RelayCommand which calls my create point handler in my vm which creates a command and executes it:
private void CreatePointHandler(MouseEventArgs e)
{
AddConnectionPointCommand addConnectionPointCommand = new AddConnectionPointCommand(this, e);
PetriNetViewModel.ExecuteCommand(addConnectionPointCommand);
}
Furthermore for an already existing point, I got a Move handler aswell (in another vm tho):
public void MovePointHandler(ConnectionPoint pointMoved, Point oldLocation, Point newLocation)
{
Vector move = new Vector(newLocation.X - oldLocation.X, newLocation.Y - oldLocation.Y);
PetriNetViewModel.ExecuteCommand(new MoveDragCanvasElementsCommand(new ConnectionPoint[] { pointMoved }, move));
}
Adding and moving a point afterwards is just working as expected.
Now i want to give the user the possibility to add and move a point in one step. In my CreatePointHandler i can figure out if the left mouse buttin it still pressed like this:
if (e.LeftButton == MouseButtonState.Pressed) {
}
but how would I move the point now? The MovePointHandler is called by an event in the codebehind (I know this shouldnt be done in mvvm, but my collegues and I think it's ok if you don't have too much code in it), which is also passing an ElementsMovedEventArgs which I dont have here.
thanks for your help in advance.
It's hard to say without seeing your codebehind that calls these handlers.
I would have thought you should have a concept of a SelectedPoint, at which you can always check if there is an intended drag happening when the mouse is moved.
i.e.
private ConnectionPoint SelectedPoint { get; set; }
private void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
// dragging something.. check if there is a point selected
if (SelectedPoint != null)
{
_viewModel.MovePointHandler(SelectedPoint, _oldLocation, _newLocation);
}
}
}
Then, as part of your CreatePointHandler, you immediately set the newly created instance to the SelectedPoint, until a MouseUp is detected.
private void ExecuteAddConnectionPointCommand()
{
// standard logic...
ConnectionPoint addedPoint = ...
SelectedPoint = addedPoint;
}
The specifics of the implementation will likely change depending on your architecture, but hopefully you get the point.
I am trying to capture the PanGesture event, so when the user swipe with his/her finger anywhere in the view to the right, for example, the RightView shows up and when to left, the LeftView shows up.
I am using the code below. It is from Xamarin docs. I modified it a bit. My 'currView' is my outlet for the current View. When I swipe at the simulator with my mouse, the event gets captured correctly, but when stepping through (after break pointing) at the curly bracket of
if (recognizer.State != (UIGestureRecognizerState.Cancelled | UIGestureRecognizerState.Failed
{
The code loops between it and this one.
HandleDrag(gesture))
once for every pixel (I think).
I am trying to trigger the load of another view, but didn't find a spot to place it at. I tried at the
Console.WriteLine ("Here")
and at:
currView.AddGestureRecognizer (gesture);
but every time the code finishes the loop, it just ignores it and execution ends. How can I make the loading of next view work?
The line of the comment:
// NEED TO LOAD ANOTHER VIEW HERE
shows my desires spot to load another View.
Here is my code
namespace Example_Touch.Screens.iPhone.GestureRecognizers
{
using System;
using System.Drawing;
using MonoTouch.Foundation;
using MonoTouch.UIKit;
public partial class GestureRecognizers_iPhone : UIViewController
{
private RectangleF originalImageFrame = RectangleF.Empty;
// The IntPtr and initWithCoder constructors are required for items that need
// to be able to be created from a xib rather than from managed code
public GestureRecognizers_iPhone(IntPtr handle)
: base(handle)
{
Initialize();
}
[Export("initWithCoder:")]
public GestureRecognizers_iPhone(NSCoder coder)
: base(coder)
{
Initialize();
}
public GestureRecognizers_iPhone()
: base("GestureRecognizers_iPhone", null)
{
Initialize();
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
WireUpDragGestureRecognizer();
}
protected void WireUpDragGestureRecognizer()
{
// create a new tap gesture
UIPanGestureRecognizer gesture = new UIPanGestureRecognizer();
// wire up the event handler (have to use a selector)
gesture.AddTarget(() => HandleDrag(gesture));
currView.AddGestureRecognizer (gesture);
}
protected void HandleDrag(UIPanGestureRecognizer recognizer)
{
PointF offset2 = recognizer.TranslationInView(currView);
// if it's just began, cache the location of the image
if (recognizer.State != (UIGestureRecognizerState.Cancelled | UIGestureRecognizerState.Failed
| UIGestureRecognizerState.Possible))
{
Console.WriteLine ("Here");
// NEED TO LOAD ANOTHER VIEW HERE
}
}
private void Initialize()
{
}
}
}
From Apple documentation:
A panning gesture is continuous. It begins (UIGestureRecognizerStateBegan) when the minimum number of fingers allowed (minimumNumberOfTouches) has moved enough to be considered a pan. It changes (UIGestureRecognizerStateChanged) when a finger moves while at least the minimum number of fingers are pressed down. It ends (UIGestureRecognizerStateEnded) when all fingers are lifted.
Check for UIGestureRecognizerState.Ended state and if it occurs, you can load another view.
From your description it seems that you could use UISwipeGestureRecognizer instead of UIPanGestureRecognizer. The UISwipeGestureRecognizer is a discrete gesture, so you don't have to check for states, you just implement it this way:
Somewhere in your code implement the handler:
private void HandleSwipeRight()
{
// load another view
Console.WriteLine("Swipe right.");
}
And initialize the gesture recognzier in ViewDidLoad():
UISwipeGestureRecognizer swipeGestureRecognizerRight = new UISwipeGestureRecognizer(HandleSwipeRight);
swipeGestureRecognizerRight.Direction = UISwipeGestureRecognizerDirection.Right;
this.View.AddGestureRecognizer(swipeGestureRecognizerRight);
You can add another swipe gesture recognizer for a different direction.