I'm trying to draw a Rectangle object for my game with a different colour so it looks like it's opened.
Basically when I run the program, it should render the doors as grey rectangles and then switch their colour after 10 seconds. They should then 'close', returning back to their original colour, after 3 seconds of being open. However, they start off as the wrong colour and then don't change at all.
I looked into using a state machine to determine whether or not the door object is open or closed, and then calling a method in Draw() to either draw it as a blue rectangle or a grey rectangle.
My enum for _doorStates:
enum doorState
{
Open,
Closed,
}
doorState[] _doorStates = new doorState[50];
This is my code in Update() that determines whether or not the door needs to be opened. I have an array of door objects and an array of _doorStates. I know this isn't the most efficient way of doing it but it's a bit late to change it!
for (int i = 0; i < doorNumber; i++)
{
if (doors[i].doorState == false)//if the object's bool value says it is closed
{
if (timeElapsed % doors[i].doorOpenRate == 0 && timeElapsed >= doors[i].doorOpenRate)//is it time for the door to open?
{
doors[i].doorState = true;
_doorStates[i] = doorState.Open;
timeOpened = timeElapsed;
}
}
else
{
if (timeElapsed - timeOpened == 3)//has it been open for 3 seconds?
{
doors[i].doorState = false;
_doorStates[i] = doorState.Closed;
}
}
}
This is the code inside the Draw() method:
for (int i = 0; i < doorNumber; i++)
{
switch(_doorStates[i])
{
case doorState.Open:
drawOpenDoor(doors[i], gameTime);
break;
case doorState.Closed:
drawClosedDoor(doors[i], gameTime);
break;
}
}
And these are my separate draw methods:
private void drawOpenDoor(door door, GameTime deltaTime)
{
spriteBatch.Draw(rect, door.doorObject, Color.CornflowerBlue);
}
private void drawClosedDoor(door door, GameTime deltatime)
{
spriteBatch.Draw(rect, door.doorObject, Color.DarkGray);
}
This is my constructor for the door objects:
public door (int xPos, int yPos, int width, int height, int orientation, int openRate, int openDuration)
{
if (orientation == 0)
{
doorObject = new Rectangle(xPos, yPos, width, height);
}
else
{
doorObject = new Rectangle(xPos + 15, yPos, height, width);
}
doorOrientation = orientation;//horizontal = 0, vertical = 1
doorOpenRate = openRate;
doorOpenDuration = openDuration;
doorState = false;
}
This is my first post so let me know if you need any more info like how the classes work. I used this post for creating the state machine.
Related
I want to change GUI button highlighted color when I press the button. However, I do not find how can I change in my Script onGUI() method.
I did not show the whole code. The necessary part is below.
Here is onGUI() method;
if-else checks the button is clicked or not.
if(GUI.Button(new Rect(currentPart.DrawDepth * spacing + x + subPartsSpacing, y, 200, 20), currentPart.EnglishTitle,myStyle))
{
if (!currentPart.IsClicked)
{
currentPart.IsClicked = true;
HumanBodyVisualizer.ShowMode showModeFullBody = HumanBodyVisualizer.ShowMode.Invisible;
bodyVisualizer.ShowBody(showModeFullBody);
AllSubPartsAndRoot.Insert(AllSubPartsAndRoot.Count, currentPart);
addAllSubPartsOfClickButton(currentPart, AllSubPartsAndRoot, AllSubPartsAndRoot.Count - 1);
HumanBodyVisualizer.ShowMode showModeCurrentPart = HumanBodyVisualizer.ShowMode.LowTransparent;
for (int i = 0; i < AllSubPartsAndRoot.Count; i++)
{
bodyVisualizer.ShowBodyPart(showModeCurrentPart, AllSubPartsAndRoot[i]);
}
}
else
{
currentPart.IsClicked = false;
List<HumanBodyPart> RemoveBodyParts = new List<HumanBodyPart>();
RemoveBodyParts.Insert(0,currentPart);
addAllSubPartsOfClickButton(currentPart, RemoveBodyParts, 1);
for(int i = 0; i < RemoveBodyParts.Count; i++)
{
if (AllSubPartsAndRoot.Contains(RemoveBodyParts[i]))
{
bodyVisualizer.ShowBodyPart(HumanBodyVisualizer.ShowMode.Invisible, RemoveBodyParts[i]);
AllSubPartsAndRoot.Remove(RemoveBodyParts[i]);
}
}
if(AllSubPartsAndRoot.Count == 0)
{
bodyVisualizer.ShowBody(HumanBodyVisualizer.ShowMode.LowTransparent);
}
else
{
for (int ii = 0; ii < AllSubPartsAndRoot.Count; ii++)
{
bodyVisualizer.ShowBodyPart(HumanBodyVisualizer.ShowMode.LowTransparent, AllSubPartsAndRoot[ii]);
}
}
}
}
You are using a custom GUIStyle.
You can change the pressed style via the onActive
Rendering settings for when the element is turned on and pressed down.
and the active
Rendering settings for when the control is pressed down.
components of the GUIStyle
But you would need a Texture2D for it in
myStyle.onActive.background = SomeTexture2D;
unfortunately there is no way to directly change the color tint of a GUIStyle.
Note: In order to configure your GUIStyle more easely you can simply create an asset in the ProjectView → right click → Create → GUISkin
Do all your settings here via the Inspector and simply reference this GUISkin asset in your EditorScript and use GUI.skin = myReferencedGuiSkin;
Alternatively you can try and use the button to turn on a flag.
You could maybe use Event.current and check if it's type is EventType.MouseUp or the mousePosition is outside of the rect you use for the button in order to reset the flag. (Not sure if this will work though)
private bool pressed;
private bool hovered;
...
public void OnGUI()
{
var color = GUI.color;
var rect = new Rect(...);
var ev = Event.current;
hovered = ev.mousePosition.x > rect.x && ev.mousePosition.x < rect.x + rect.width && ev.mousePosition.y > rect.y && ev.mousePosition.y < rect.y + rect.height;
if (Event.current.type == EventType.MouseUp) pressed = false;
else if (ev.type == EventType.MouseDown) pressed = true;
if (hovered && pressed) GUI.color = Color.green;
if (GUI.Button(rect, ...))
{
...
}
GUI.color = color;
// in order to receive the mouse events
Repaint();
}
If this is an EditorWindow you have to make sure wantsMouseMove is set to true in order to receive mouse events.
I am trying to create a list of buttons using information collected from www forms in my program. The problem I am having is that when I create the buttons, it infinitely creates them, and thusly unity crashes. I have two theories as to why this is happening, the first being that the function is being called each frame and is therefore creating too many buttons and the second is that for some reason when I check the length of a multidimensional array it has a length of 33 when it should only have a length of 3 (it should have 3 arrays of arrays). The whole script is below.
using Boo.Lang;
using System.Collections;
using SCG = System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using System;
public class MeetingRequestViewer : MonoBehaviour
{
//This script contains the code on how to display information about the students to the staff
//it displays each student in a different row
private Rect windowRect = new Rect(0, 0, Screen.width, Screen.height);
public Vector2 scrollPosition = Vector2.zero;
private int BSpace;
public string[,] SortedStudentArray;
private int j;
private string[][] MeetRequests;
private string[] temp;
private SCG.List<string> H_Priority = new SCG.List<string>();
private SCG.List<string> M_Priority = new SCG.List<string>();
private SCG.List<string> L_Priority = new SCG.List<string>();
private SCG.List<string> FullList = new SCG.List<string>();
private SCG.List<string> UAList;
private SCG.List<object> StudentButtonList = new SCG.List<object>();
private string[] Holding = new string[5];
private string[] SearchTerms;
public int ButtonSpacing = 10;
public int ButtonWidth = 80;
public int ButtonHeight = 30;
public void OnGUI()
{
//create a window
GUI.Window(0, windowRect, WindowFunction, "Meeting Request Viewer");
}
public void WindowFunction(int windowID)
{
//Fetches all user Data
string[][] userArray = GetComponent<Userdata>().CallDetail();
string[][] StudentArray = GetComponent<Userdata>().UserSorting(userArray);
//Calls the SortStudentArray method
string[,] SortedStudentArray = SortStudentList();
//Creates a box with a scrolling bar to taverse the y axis
scrollPosition = GUI.BeginScrollView(new Rect(Screen.width / 6, Screen.height / 6, 350, 250), scrollPosition, new Rect(0, 0, 300, 40 * SortedStudentArray.Length));
//for each row in the sorted student array
for (int x = 0; x < SortedStudentArray.Length - 1; x++)
{
//This keeps the gap between each button consistent
var y = ButtonSpacing + ButtonHeight;
//Regular Meeting Request
if (SortedStudentArray[x, 7] == "1")
{
//Urgent Meeting Request
if (SortedStudentArray[x, 8] == "1")
{
Debug.Log("Even Here");
//Create Button coloured red
GUI.backgroundColor = Color.red;
}
//Regular Meeting Request
else
{
//Create Button coloured yellow
GUI.backgroundColor = Color.yellow;
}
}
//No Meeting Request
else
{
//Create Button coloured green
GUI.backgroundColor = Color.green;
}
GUI.Button(new Rect(ButtonSpacing, ButtonSpacing + x * y, ButtonWidth, ButtonHeight), SortedStudentArray[x, 6]);
}
GUI.EndScrollView();
ButtonsCreated = true;
}
private string[,] SortStudentList()
{
//This method is used to fetch the meeting request data, split it into a jagged array and then again so that each
//row is a new user and each item in the row is a different peice of information relating to the user
//The Jagged array is then sorted using the bubble sort algorithm so that the students that require urgent meetings
//appear at the topo of the request table
//call website with the data store
WWW MeetReqData = new WWW("http://localhost/Wellbeing/meetingrequest.php");
//until WWW is finished do nothing
while (MeetReqData.isDone == false)
{
}
//convert the returned value into a string
string MeetReqString = MeetReqData.text;
//split the text into a string list
string[] mrq = MeetReqString.Split(';');
//convert the string list into a jagged array
MeetRequests = new string[mrq.Length][];
int i = 0;
//for each item in the list
foreach (string s in mrq)
{
//split it into its individual peice of information
string[] g = s.Split('|');
//store the information about a user on a new line
MeetRequests[i] = g;
++i;
}
for (int n = 0; n < MeetRequests.Length - 1; n++)
{
if (MeetRequests[n][1] == "1" && MeetRequests[n][2] == "1")
{
H_Priority.Add(MeetRequests[n][0]);
}
else if (MeetRequests[n][1] == "1" && MeetRequests[n][2] == "0")
{
M_Priority.Add(MeetRequests[n][0]);
}
else
{
L_Priority.Add(MeetRequests[n][0]);
}
}
//Combines all lists into a larger list of priorities
FullList.AddRange(H_Priority);
FullList.AddRange(M_Priority);
FullList.AddRange(L_Priority);
//convertFullList into an array for easier mainpulation and comparisons
string[][] feelingsArray = GetComponent<Userdata>().CallFeelings();
//FullList only contains 3 values
Debug.Log(FullList.Count);
//Info array about each user
string[,] SortedStudentArray = new string[FullList.Count, 11];
//SortedStudentArray contains 33 rows
Debug.Log("This thing Here");
Debug.Log(SortedStudentArray.Length);
//Line Counter
int SSAPos = 0;
// For every element in FullList
foreach (var item in FullList)
{
Debug.Log(FullList.Count);
//For each user in userArray
for (int y = 0; y < Userdata.userArray.Length; y++)
{
if (Userdata.userArray[y][0] == item)
{
for (int n = 0; n < Userdata.userArray; n++)
SortedStudentArray[SSAPos, n] = Userdata.userArray[y][n];
break;
}
}
Debug.Log(SortedStudentArray.Length);
//For each user in userArray
for (int y = 0; y < MeetRequests.Length; y++)
{
if (MeetRequests[y][0] == item)
{
SortedStudentArray[SSAPos, 7] = MeetRequests[y][1];
SortedStudentArray[SSAPos, 8] = MeetRequests[y][2];
break;
}
}
Debug.Log(SortedStudentArray.Length);
SSAPos += 1;
}
return SortedStudentArray;
}
// Update is called once per frame
void Update ()
{
//if (GUI.Button(new Rect(Screen.width / 4, Screen.height / 7, Screen.width / 2, Screen.height / 8), "Log Out"))
//{
// Debug.Log("Logged Out");
// SceneManager.LoadScene("Login");
//}
}
I did try to use a while loop around the section of code however it made no difference to the creation of buttons, they were still created more times than were needed
FullList is continually being populated which will eventually lead to a crash.
Call SortStudentList in Awake, such as;
string[,] SortedStudentArray;
void Awake ()
{
//Calls the SortStudentArray method
SortedStudentArray = SortStudentList();
}
Then;
public void WindowFunction(int windowID)
{
// Prevent further execution until SortedStudentArray is ready
if (SortedStudentArray == null) return;
...
}
From the documentation:
OnGUI is called for rendering and handling GUI events.
This means that your OnGUI implementation might be called several
times per frame (one call per event). For more information on GUI
events see the Event reference. If the MonoBehaviour's enabled
property is set to false, OnGUI() will not be called.
You need to reevaluate how you're doing your GUI. As mentioned in their GUI scripting guide you should probably use their UI system.
You especially don't want to be making one or more WWW calls per frame, or doing a bunch of sorting every frame.
https://docs.unity3d.com/ScriptReference/MonoBehaviour.OnGUI.html
OnGUI is called for rendering and handling GUI events.
This means that your OnGUI implementation might be called several times per frame (one call per event). For more information on GUI events see the Event reference. If the MonoBehaviour's enabled property is set to false, OnGUI() will not be called.
You should probably use another way to call your method, or use a boolean value to check whether or not it has already been called.
Please refer to the image below to better understand the Unity lifecycle methods.
More information about Unity's Lifecycle can be found at: https://docs.unity3d.com/Manual/ExecutionOrder.html
I have an app where I am drawing lines through points to form polygons, I want to be able to have the four corners of the polygon be moveable, I have this code inside of a custom view I've created for drawing polygons:
public override bool OnTouchEvent(MotionEvent e)
{
MotionEventActions action = e.Action;
float x = 0.0f;
float y = 0.0f;
float radius = 20.0f;
// Get the current pointer index
int tempPointerIndex = e.ActionIndex;
x = e.GetX(tempPointerIndex);
y = e.GetY(tempPointerIndex);
// For each point, check to see if the touch fell within the bounds of the circle
for (int i = 0; i < _points.Length; i++)
{
if((x >= _points[i].X - radius && x <= _points[i].X + 20) ||
(y >= _points[i].Y - radius && y >= _points[i].Y + 20))
{
switch (action)
{
case MotionEventActions.Down:
{
// Get the current x/y when not moving so we can calculate the delta x/y in
// move events
lastX = x;
lastY = y;
// Set the global active pointer index
activePointerId = e.GetPointerId(0);
break;
}
case MotionEventActions.Move:
{
int pointerIndex = e.FindPointerIndex(activePointerId);
x = e.GetX(pointerIndex);
y = e.GetY(pointerIndex);
// Find the delta x/y
float deltaX = x - lastX;
float deltaY = y - lastY;
// Move the selected skew handle by the delta x/y
_points[i].X += deltaX;
_points[i].Y += deltaY;
// Force a redraw
this.Invalidate();
break;
}
case MotionEventActions.Up:
{
// Invalidate the active pointer index
activePointerId = global::Android.Views.MotionEvent.InvalidPointerId;
break;
}
case MotionEventActions.Cancel:
{
// Invalidate the active pointer index
activePointerId = global::Android.Views.MotionEvent.InvalidPointerId;
break;
}
case MotionEventActions.PointerUp:
{
int pointerIndex = e.ActionIndex;
int pointerId = e.GetPointerId(pointerIndex);
if(pointerId == activePointerId)
{
// Active pointer going up, choose new active pointer and adjust accordingly
int newPointerIndex = pointerIndex == 0 ? 1 : 0;
lastX = e.GetX(newPointerIndex);
lastY = e.GetY(newPointerIndex);
activePointerId = e.GetPointerId(newPointerIndex);
}
break;
}
}
}
}
return base.OnTouchEvent(e);
}
The case for MotionEventActions.Move never seems to get hit, in fact, the only event that seems to get hit is the down event when I initially hit a point. Also, the condition that checks the bounds of the point works for sure, I've tested it numerous times, so that isn't the issue either, I'm not sure if maybe I'm checking for the event incorrectly, but as far as I can tell this is how both the Xamarin and Android documentation say to handle this event, so I'm not really sure what the problem is, or how to find it. My question is, what exactly am I doing wrong that is causing the move event to not be picked up?
Once you determine if one of your points is in range of the user's touch, start returning true from OnTouchEvent until you get an Up, that way all future touch events become Move events on your custom View and are not redirected to the View's parent.
Down/Move/Up touch event logic:
public override bool OnTouchEvent(MotionEvent e)
{
x = e.GetX(e.ActionIndex);
y = e.GetY(e.ActionIndex);
Log.Debug(TAG, $"{e.Action} : {x} : {y}" );
if (e.Action == MotionEventActions.Down)
{
if (true) // some check if the x/y touch is on or near a moveable point, isDraggingPoint becomes true
isDraggingPoint = true;
else
isDraggingPoint = false;
}
if (e.Action == MotionEventActions.Move)
{
// move your Point, x/y touch for draggingPoint, update display, etc...
}
if (e.Action == MotionEventActions.Up)
{
// save the final x/y touch for draggingPoint, update display, etc...
isDraggingPoint = false;
}
return isDraggingPoint;
}
Output:
[CustomView] Down : 358.5332 : 234.6167
[CustomView] Move : 358.5332 : 234.6167
[CustomView] Move : 352.5305 : 238.6241
[CustomView] Move : 334.8269 : 237.3517
[CustomView] Move : 360.5305 : 246.6073
[CustomView] Move : 360.5305 : 246.6073
[CustomView] Up : 360.5305 : 246.6073
I am new to programming and new to c#, yet I am trying to make a 2D game. I created a Background class and a Close class, the Close class is for an exit button that I want to implement. What I want to achieve from this is that when I release the close button, the background will gradually lower is tone from white to a darker white. The problem is that I don't know how to really code this.Here is a view to my code.
Close Class
private Texture2D texture;
private Vector2 position;
private Rectangle bounds;
private Color color;
MouseState oldstate;
public Close(Texture2D texture, Vector2 position){
this.texture = texture;
this.position = position;
bounds = new Rectangle((int)position.X, (int)position.Y, texture.Width, texture.Height);
color = new Color(40, 40, 40);
}
public void Update(GameTime gameTime){
MouseState state = Mouse.GetState();
Point point = new Point(state.X, state.Y);
if(bounds.Contains(point) && !(state.LeftButton == ButtonState.Pressed)){
color = new Color(235, 50, 50);
} else if((!(bounds.Contains(point)) && (state.LeftButton == ButtonState.Pressed)) || (!(bounds.Contains(point)) && (state.LeftButton == ButtonState.Released))){
color = new Color(40, 40, 40);
}
if((state.LeftButton == ButtonState.Pressed) && (oldstate.LeftButton == ButtonState.Released) && (bounds.Contains(point))){
color = new Color(172, 50, 50);
}
if((state.LeftButton == ButtonState.Released) && (oldstate.LeftButton == ButtonState.Pressed) && (bounds.Contains(point))){
}
oldstate = state;
}
public void Draw(SpriteBatch spriteBatch){
spriteBatch.Draw(texture, position, color);
}
Background Class
public Color color;
public Background(Color color){
this.color = color;
}
public void Update(GameTime gameTime){
}
Just to be a little more specific, I want the color to change within the Background class and be able to call this through the Close class. Also, keep in mind that the background color is being specified in the Game1 class, and the Update method is being called from it too.
Anyway, any help will be appreciated.
What I would do is something like this, though I don't have a lot of experience with Forms so it may or may not work as expected.
You'd call SyncFadeOut()/AsycFadeOut() through your Close class as required.
Synchronous (blocking) version:
public void SyncFadeOut()
{
// define how many fade-steps you want
for (int i = 0; i < 1000; i ++)
{
System.Threading.Thread.Sleep(10); // pause thread for 10 ms
// ----
// do the incremental fade step here
// ----
}
}
Async (non-blocking) version:
System.Timers.Timer timer = null;
public void FadeOut(object sender, EventArgs e)
{
// ----
// do the incremental fade step here
// ----
// end conditions
if ([current_color] <= [end_color])
{
timer.Stop();
// trigger any additional things you want, like close window
}
}
public void AsyncFadeOut()
{
System.Timers.Timer timer = new System.Timers.Timer(10); // triggers every 10ms, change this if you want a faster/slower fade
timer.Elapsed += new System.Timers.ElapsedEventHandler(FadeOut);
timer.AutoReset = true;
timer.Start();
}
I have a bunch of coordinates and I draw them as a coordinate system on a bitmap image. While hovering my mouse over my image, I want to highlight the coordinates that are within certain Distance from my MousePosition using the below routine:
public void HighLightNearbyDots(Point _mousePosition)
{
// ..
foreach (var point in myDisplayedCoords)
{
Distance = (int)(MousePosition - point); // this gets the distance between two points and converts it to an int
if (Distance < 10)
point.Color = Colors.Red;
else
point.Color = InitialCoordColor;
}
DrawImage();
}
Now, if I find a Nearby coordinates, I want to Change my CursorShape to be a HAND. I can not do that inside if (Distance < NearbyCursorDistance); Why? Because I will either need to change back to Arrow (which takes a micro second) on the else and user won't see it, or it will stay as HAND for the rest of execution. So I implement this:
private bool IsThereANearbyDot(CoordPoint _mousePosition)
{
foreach(var point in MyDisplayedCoords)
{
if (10 > (int) (_mousePosition - point))
return true;
}
return false;
}
And use it this way:
public void HighLightNearbyDots(Point _mousePosition)
{
//..
if (IsThereANearbyDot(MousePosition))
CursorShape = Cursors.Hand;
else
CursorShape = Cursors.Arrow;
foreach (var point in myDisplayedCoords)
{
Distance = (int)(MousePosition - point);
if (Distance < 10)
point.Color = Colors.Red;
else
point.Color = InitialCoordColor;
}
DrawImage();
}
It works but if MyDisplayedCoords is huge, which I have to iterate twice now, it takes a lot of time and users will notice lagging on the screen. How could I solve this problem?
All you need to do is set the cursor to the default (arrow) at the start of your HighLightNearbyDots() method and then set to hand in the distance < 10 clause (and not the else). E.g.:
public void HighLightNearbyDots(Point _mousePosition)
{
// ..
CursorShape = Cursors.Arrow;
foreach (var point in myDisplayedCoords)
{
Distance = (int)(MousePosition - point); // this gets the distance between two points and converts it to an int
if (Distance < 10)
{
CursorShape = Cursors.Hand;
point.Color = Colors.Red;
}
else
point.Color = InitialCoordColor;
}
DrawImage();
}
You shouldn't have any problems with the cursor flickering, but if you do then you could set a flag and just set the cursor once in the method, like this:
public void HighLightNearbyDots(Point _mousePosition)
{
// ..
bool handCursor = false;
foreach (var point in myDisplayedCoords)
{
Distance = (int)(MousePosition - point); // this gets the distance between two points and converts it to an int
if (Distance < 10)
{
handCursor = true;
point.Color = Colors.Red;
}
else
point.Color = InitialCoordColor;
}
CursorShape = handCursor ? Cursors.Hand : Cursors.Arrow;
DrawImage();
}
This will ensure that the cursor is resolved each time you execute the HighLightNearbyDots() method (which presumably you are doing periodically, e.g. on mouse-move events), and that it will be the default cursor unless you've highlighted any dots, in which case it will be the hand.
If that does not address your question, please provide a good Minimal, Complete, and Verifiable example that shows clearly what you're doing, along with a precise explanation of what the code does and what you want it to do instead, as well as what specifically you are having trouble figuring out.