I recently started to make video games using the XNA game studio 4.0. I have made a main menu with 4 sprite fonts using a button list. They change color from White to Yellow when I press the up and down arrows.
My problem is that when I scroll through it goes from the top font to the bottom font really fast and goes straight to the last one. I am unsure why this is? Is it because I am putting it in the update method and it is calling it every 60 seconds or so?
Here is my code for when I press the arrow keys.
public void Update(GameTime gameTime)
{
keyboard = Keyboard.GetState();
if (CheckKeyboard(Keys.Up))
{
if (selected > 0)
{
selected--;
}
}
if (CheckKeyboard(Keys.Down))
{
if (selected < buttonList.Count - 1)
{
selected++;
}
}
keyboard = prevKeyboard;
}
public bool CheckKeyboard(Keys key)
{
return (keyboard.IsKeyDown(key) && prevKeyboard.IsKeyUp(key));
}
I need someone to help me slow it down to a reasonable speed.
If you could help me it would be greatly appreciated.
I think the issue is because you are not setting prevKeyboard correctly.
Try this:
public void Update(GameTime gameTime)
{
keyboard = Keyboard.GetState();
if (CheckKeyboard(Keys.Up))
{
if (selected > 0)
{
selected--;
}
}
if (CheckKeyboard(Keys.Down))
{
if (selected < buttonList.Count - 1)
{
selected++;
}
}
prevKeyboard = keyboard; // <=========== CHANGE MADE HERE
}
public bool CheckKeyboard(Keys key)
{
return (keyboard.IsKeyDown(key) && prevKeyboard.IsKeyUp(key));
}
I think its because
if (CheckKeyboard(Keys.Up))
{
if (selected > 0)
{
selected--;
// This loops executes so quick before you release button.
// Make changes here to stop the loop if the button is pressed and loop
// executed once.(May be) just **return;** would do ?
}
}
if (CheckKeyboard(Keys.Down))
{
if (selected < buttonList.Count - 1)
{
selected++;
// This loops executes so quick before you release button.
// Make changes here to stop the loop if the button is pressed and loop
// executed once.(May be) just **return;** would do ?
}
}
Related
I am making an app for a school project, and am working with showing/hiding images to make an 'upgrade' and 'downgrade' button. I have made it so when you press a button, it updates the image. At first I had this with structuring my code differently. The issue with that was the fact it wouldnt load my current picture state in again after I left and re-entered the scene (although it would save the value of the upgradelevel). When I tried to restructure it, nothing works. What am I doing wrong here (if it can even be done like this...)?
I had structured the code differently at first, but came to the issue of my image not loading when I re-entered the scene. Structuring the code like this I felt it would bypass that issue. Now another issue has come up.
public GameObject woodWeapon;
public GameObject stoneWeapon;
public GameObject metalWeapon;
public static int weaponLevel = 0;
public void upgradeWeapon()
{
weaponLevel += 1;
if (weaponLevel > 3)
{
weaponLevel = 3;
}
}
public void downgradeWeapon()
{
weaponLevel -= 1;
if (weaponLevel < 0)
{
weaponLevel = 0;
}
}
public void Update()
{
if (weaponLevel == 0)
{
woodWeapon.SetActive(false);
stoneWeapon.SetActive(false);
metalWeapon.SetActive(false);
}
else if (weaponLevel == 1)
{
woodWeapon.SetActive(true);
stoneWeapon.SetActive(false);
metalWeapon.SetActive(false);
}
else if (weaponLevel == 2)
{
woodWeapon.SetActive(false);
stoneWeapon.SetActive(true);
metalWeapon.SetActive(false);
}
else if (weaponLevel == 3)
{
woodWeapon.SetActive(false);
stoneWeapon.SetActive(false);
metalWeapon.SetActive(true);
}
}
Expected result: Pressing 'upgrade' button updates my screen with am image, a wood sword instead of nothing, a stone sword instead of a wood sword, etc...
pressing 'downgrade' should do the same thing, but the other way around
Reality: Pressing either 'upgrade' or 'downgrade' does nothing to the image.
I'm completely stumped by this one. I have a book in game and when you are looking in its general direction while within a certain distance of it and press the Action button (e or left mouse click), it's supposed to display a UI panel (bookPage). Here's my code for it.
void Update () {
Vector3 direction = player.transform.position - this.transform.position;
angle = Vector3.Angle (direction, player.transform.forward);
distance = direction.magnitude;
if (angle >= 160 && distance <= 2 && !bookPage.activeSelf) {
if(Input.GetButtonDown("Action")) {
bookPage.SetActive (true);
}
}
if (bookPage.activeSelf && Input.GetButtonDown("Action")) {
bookPage.SetActive (false);
}
}
This doesn't work. Specifically, the line to set the page to active doesn't work. If it's open, it will close correctly. If I copy and paste bookPage.SetActive (true);
anywhere within this method besides within if(Input.getButtonDown("Action")){}, it will make the bookPage active. If I put Debug.Log("message"); within that if statement though, it will show up in the console. It's just the one line that sets bookPage to active that isn't working. I have no idea why. I've tried moving it to a different method then calling it and doing the same but using a Coroutine. Neither worked. I also tried using other keys which did work, but I need it to work with the action key. It's also worth noting that the action key works in other usage. I already use it to open doors. Has anyone else run into a problem like this?
When you call SetActive(true), activeSelf will return true for your second condition which will invoke SetActive(false) immediately after.
Change this;
if (angle >= 160 && distance <= 2 && !bookPage.activeSelf)
{
if (Input.GetButtonDown("Action"))
{
bookPage.SetActive(true);
}
}
if (bookPage.activeSelf && Input.GetButtonDown("Action"))
{
bookPage.SetActive(false);
}
To this;
if (angle >= 160 && distance <= 2 && !bookPage.activeSelf)
{
if (Input.GetButtonDown("Action"))
{
bookPage.SetActive(true);
}
}
else if (bookPage.activeSelf && Input.GetButtonDown("Action"))
{
bookPage.SetActive(false);
}
I have an animatormanager able to create run time animation from 1 animation (start frame, end frame and delay then the speed is calculated, will do morphing and more later). My unity version is 5.3.1f1
Everything work fine, some stuff are not done but are optional. The animator can queue requested animation to prevent interupting the current one.
The thing is, I can only start 1 time an animation with an object. The code to launch the next animation is exactly the same but there is just nothing to do.
Event if its 2 time the same animation with the same animation data
All formula are good and tested.
I did intensive debugging with breakpoint to make sure everything is fine at any point
Is there something to prevent me from starting an animation 2 time on an object one after an other?. I have no error or warning at all. The first animation work fine no matter the settings I put in my AnimData struct, but the second time, nothing happen.
Here is the essential:
public int? startAnim(int index)
{
if(index < animIndex.Count)
{
startAnim(animIndex[index]);
}
return null;
}
//private because the struct is internal, this make sure the animator keep control of the list.
private int? startAnim(AnimData animD)
{
if(locked)
{
#if UNITY_EDITOR
Debug.Log("anim locked");
#endif
return null;
}
//current anim (queue anim) not finished
if(endTime > Time.time)
{
if(canQueue)
{
addToQueue(animD);
return animDataQ.Count;
}
else
{
return null;
}
}
else
{
endTime = Time.time + animD.TotalTime;
StartCoroutine(animManager(animD));
return 0;
}
return null;
}
#endregion anim starters
private IEnumerator animManager(AnimData animData)
{
animator.speed = Mathf.Abs(animData.calculateSpeed(animLength, AnimType.main, currentKey).Value);
//animator.Play(0,0, animData.StartKey/animLength);
if(animData.AnimSpeed > 0)
{
animator.Play(0,0, animData.StartKey/animLength/2);
}
else
{
//animator.Play(0,0, (animLength * 2) - (animData.StartKey/animLength));
animator.Play(0,0, (((animLength*2) - animData.StartKey)/(animLength * 2)));
}
//animator.Play(0,0, (animData.AnimSpeed > 0) ? animData.StartKey/animLength : ((animLength * 2) - (animData.StartKey/animLength)));
yield return new WaitForSeconds(animData.Delay);
animator.Stop();
yield return null;
endAnim();
}
private void addToQueue(AnimData animD)
{
animDataQ.Enqueue(animD);
endTime += animD.TotalTime;
queueTime += animD.TotalTime;
}
private void endAnim()
{
if(canQueue && animDataQ.Count > 0)
{
StartCoroutine(animManager(animDataQ.Dequeue()));
}
}
Thanks for your time.
i found a work around.
I dont want to mark it as an accepted solution since its not "clean" in my opinion
there is certainly some kind of problem with the animator for some specific case like mine.
my solution was to use animator.speed = 0 instead of animator.stop();
everything worked instantly with that small change
i will ask my question to the unity forum because there is something strange for sure
I'm trying to have the Enter key cause a new 'map' to generate for my game, but for whatever reason after implementing full-screen in it the input check won't work anymore. I tried removing the new code and only pressing one key at a time, but it still won't work.
Here's the check code and the method it uses, along with the newMap method:
public class Game1 : Microsoft.Xna.Framework.Game
{
// ...
protected override void Update(GameTime gameTime)
{
// ...
// Check if Enter was pressed - if so, generate a new map
if (CheckInput(Keys.Enter, 1))
{
blocks = newMap(map, blocks, console);
}
// ...
}
// Method: Checks if a key is/was pressed
public bool CheckInput(Keys key, int checkType)
{
// Get current keyboard state
KeyboardState newState = Keyboard.GetState();
bool retType = false; // Return type
if (checkType == 0)
{
// Check Type: Is key currently down?
retType = newState.IsKeyDown(key);
}
else if (checkType == 1)
{
// Check Type: Was the key pressed?
if (newState.IsKeyDown(key))
{
if (!oldState.IsKeyDown(key))
{
// Key was just pressed
retType = true;
}
else
{
// Key was already pressed, return false
retType = false;
}
}
}
// Save keyboard state
oldState = newState;
// Return result
return retType;
}
// Method: Generate a new map
public List<Block> newMap(Map map, List<Block> blockList, Console console)
{
// Create new map block coordinates
List<Vector2> positions = new List<Vector2>();
positions = map.generateMap(console);
// Clear list and reallocate memory previously used up by it
blockList.Clear();
blockList.TrimExcess();
// Add new blocks to the list using positions created by generateMap()
foreach (Vector2 pos in positions)
{
blockList.Add(new Block() { Position = pos, Texture = dirtTex });
}
// Return modified list
return blockList;
}
// ...
}
I never touched any of the above code for this when it broke - changing keys won't seem to fix it. Despite this, I have camera movement set inside another Game1 method that uses WASD and works perfectly. All I did was add a few lines of code here:
private int BackBufferWidth = 1280; // Added these variables
private int BackBufferHeight = 800;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferWidth = BackBufferWidth; // and this
graphics.PreferredBackBufferHeight = BackBufferHeight; // this
Content.RootDirectory = "Content";
this.graphics.IsFullScreen = true; // and this
}
When I try adding text to be displayed in the event the key is pressed, it seems that the If is never even triggered despite the correct key being pressed.
It seems that when the CheckInput method attempts to check for 'Enter' having just been pressed, it passes the first check if (newState.IsKeyDown(key)) (which returns true) but fails the second if (!oldState.IsKeyDown(key)) check. (and returns true, but shouldn't)
Rolling answer, updated
Here's how I would have wrote your code:
public class Game1 : Microsoft.Xna.Framework.Game
{
// ...
protected override void Update(GameTime gameTime)
{
// ...
// Check if Enter was pressed - if so, generate a new map
if (CheckInput(Keys.Enter, 1))
{
blocks = newMap(map, blocks, console);
}
// ...
}
// Method: Checks if a key is/was pressed
public bool CheckInput(Keys key, int checkType)
{
KeyboardState newState = Keyboard.GetState(); // Get current keyboard state
bool retType = false; // Return type
var debug_new = newState.IsKeyDown(key);
var debug_old = oldState.IsKeyDown(key);
if (checkType == 0) // Check Type: Is key currently down?
{
retType = newState.IsKeyDown(key);
}
// Should happen only once, if key wasn't pressed before
else if (checkType == 1 && newState.IsKeyDown(key))
{
// Key pressed, wasn't pressed last update -> true.
// Key pressed, but was pressed last update -> false.
retType = !oldState.IsKeydown(key);
}
oldState = newState; // Save keyboard state
return retType; // Return result
}
// ...
}
It's shorter and easier to read.
Now, when/how is your update method called from? does it happen when you hit a key? is it in a loop?
Next, If you remove (or comment out) the things you changed (in the 3rd code block), does it work?
Next, if you put a break point in your CheckInput, does it stop there everytime you hit a key?
Hopefully from there you'll be able to get some more help.
Edit:
Ok, lets see, when/why would you call the method with a CheckInput(key, 0) ?
Are you trying to avoid a double press of the Enter for example? (what's the point of those checks?)
Another edit:
Yep, I know it's being called every frame and not keypress. The only problem is I don't know know how often is that ... 60 fps vs 5 fps is a big difference.
Was wondering if there's any chance it gets out of sync somehow ... What I would have done is set a conditional debugging point there, to break on Enter pressed, released, and have a good look at your newState / oldState, to make sure the logic works the way you want (easier with the shorter code I believe :)
Fixed it by adding a line to the Update method as opposed to relying on it being updated during the CheckInput method.
Updated methods:
protected override void Update(GameTime gameTime)
{
// ...
if (CheckInput(Keys.Enter, 1))
{
blocks = newMap(map, blocks, console);
}
oldState = Keyboard.GetState();
// ...
}
// Method: Checks if a key is/was pressed
public bool CheckInput(Keys key, int checkType)
{
// Get current keyboard state
KeyboardState newState = Keyboard.GetState();
bool retType = false; // Return type
if (checkType == 0)
{
// Check Type: Is key currently down?
if (newState.IsKeyDown(key))
{
retType = true;
}
else
{
retType = false;
}
}
else if (checkType == 1)
{
// Check Type: Was the key pressed?
if (newState.IsKeyDown(key))
{
if (!oldState.IsKeyDown(key))
{
// Key was just pressed
retType = true;
}
else
{
// Key was already pressed, return false
retType = false;
}
}
}
// Return result
return retType;
}
Although I'm not entirely sure how this makes it work (especially considering it worked without this modification before) it's a solution nonetheless.
I'm having trouble with touch/multitouch input.
I want to draw a small rectangle, 100x100 in dimensions, wherever the user presses (mission accomplished) but I also want them to move as the user moves his fingers (that's not happening atm).
I'm also getting weird behaviour besides the not-moving part, let's say I touch first with my thumb, then with my middle finger. Two cubes appear bellow each finger, but if I remove the finger I place first (thumb in this scenario) the cube under the finger I placed second (middle finger) will disappear and the one where my thumb was will still be there. I guess this issue will solve itself once I get this to update correctly whenever there is movement.
This are the Draw and Update snippets. Any help appreciated:
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
TouchCollection touchLocations = TouchPanel.GetState();
i = 0;
foreach (TouchLocation touchLocation in touchLocations)
{
if (touchLocation.State == TouchLocationState.Pressed)
{
pos[i] = touchLocation.Position;
}
i++;
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin();
for (j = 0; j < i; j++)
{
spriteBatch.Draw(whiteRectangle, new Rectangle(((int)pos[j].X - 50), ((int)pos[j].Y - 50), 100, 100), Color.Chocolate);
}
spriteBatch.End();
base.Draw(gameTime);
}
I would recommend using a Dictionary assuming you want to associate touch locations to their position. The key should be the TouchLocation.Id
TouchLocation.Id will remain the same for a given interaction. There is no guarantee that the order of TouchLocations will be the same from one frame to another, so you have to use their ID and not the order they appear in the collection.
I'm a bit late on this, but well here is what was wrong and how I solve it...
foreach (TouchLocation touchLocation in touchLocations)
{
if (touchLocation.State == TouchLocationState.Pressed)
{
pos[i] = touchLocation.Position;
}
i++;
}
I guess I was sleepy when I wrote this part of the code (nothing good happens after 2 AM... not even good code) I don't know why I was checking if the location state was pressed... that's why it didn't move when I move... first frame it is indeed pressed but once you start moving it it's .Moved ... all in all, removed that if and it works like a charm...
foreach (TouchLocation touchLocation in touchLocations)
{
pos[i] = touchLocation.Position;
i++;
}
All in all now it behaves as intended.
Cheers!