The Context:
I'm currently creating a melee combo system for my 2D RPG. The first time a player attacks will trigger the first attack animation and if the player chooses to attack a second time, it ill trigger the second attack animation. I got the system to work by calling an attack function that passes a string parameter depending on which animation I need to be played.
So the ComboOrder function will be called every frame to order the animations and once the player presses the "E" key, the Attack function will be called with either "Attack-1" or "Attack-2".
The Problem: The code and animations work fine however, after many changes to other parts of the game I noticed, I get a warning every time I press the "E" key. What I changed was completely unrelated ti this so I don't see a connection from that to the warning.
Parameter" does not exist
Solutions I have tried: I've searched the web for answers, however, none of them fit my issue. It seems that Unity thinks I'm passing the string " which would indicate a syntax error but I've double-checked my code and I can't find anything of sorts. I've also heard that it has been a bug in Unity but they never told how to fix it. Is there anything I can do if that were the case?
Here's the code:
void ComboOrder() {
if (comboIndex == 1)
{
if (Input.GetKeyDown(KeyCode.E)) // Attack Input
{
Attack("Attack-1");
comboIndex++;
}
} else if (comboIndex == 2) {
if (Input.GetKeyDown(KeyCode.E)) // Attack Input
{
Attack("Attack-2");
comboIndex--;
}
}
}
public void Attack(string attack)
{
animator.SetTrigger(attack);
}
And here are the Animator settings for the player:
So after not being able to work on the game for about two weeks, I came back to take a closer look and realized it was what you said, there was an animation event calling the function without passing a parameter and since it was not coded, I never caught it. Thank you to everybody who helped.
Related
Hey StackOverflow community, I am making my very first game and I've run into a problem. The game is about a ball dodging obstacles in order to reach the end of a level. So my issue is that I want to implement a "Woosh" sound whenever the ball passes an obstacle. I have created an invisible object which is supposed to execute the "Woosh" sound whenever the ball passes the invisible object.
These are the settings on the "invisible" object:
If you look at the image you can see that there is a "Woosh" script which contains these lines of code:
using UnityEngine;
public class Woosh : MonoBehaviour
{
void OnCollisionEnter(Collision collisionInfo)
{
if (collisionInfo.collider.tag == "Player")
{
FindObjectOfType<AudioManager>().Play("Woosh");
}
}
}
This code is supposed to make the invisible object play the "Woosh" audio whenever it collides with the Player. However, when I try it out in game, the audio never gets played when I pass the invisible object.
For your information, I do have an audio manager, which contains all my sounds that I have implemented so far. It can be seen here:
Note: All the other sounds work, but not the "Woosh" sound.
Any help is appreciated!
Thanks in advance,
E.W
Comments aren't sufficient.
What I'm trying to tell you is that you can't just automatically assume that the problem is with the "Woosh" sound. That's just one of the things that can go wrong. You have this code:
public class Woosh : MonoBehaviour
{
void OnCollisionEnter(Collision collisionInfo)
{
if (collisionInfo.collider.tag == "Player")
{
FindObjectOfType<AudioManager>().Play("Woosh");
}
}
}
Your expectation is that when OnCollisionEnter is called, the "Woosh" sound will be played. You say the sound isn't playing, so you have to figure out what's wrong. This is what I would check:
Can you make the "Woosh" sound play somewhere else? You should be able to add code to your program to make the "Woosh" sound play. Maybe replace code that plays "ShatterSmall" with "Woosh". If the sound doesn't play there, then there's probably something wrong with the "Woosh" sound, and you need to fix it. If the sound does play there, then there's some other problem.
If "Woosh" plays in other places, but not in this code, then you have to check to see if the code is even being executed. If you're running this code in your debugger, put a breakpoint on the OnCollisionEnter method to see if it gets called. If it doesn't get called, then the problem is somewhere else. If you don't have a debugger, then you can output some kind of message like "Hey, I got here!" to tell you that the OnCollisionEnter function actually was called.
If you're getting to OnCollisionEnter, then you need to check to see if collisionInfo.collider.tag does indeed contain the value "Player". Again, use some kind of output function to tell you what is in that variable.
If collisionInfo.collider.tag == "Player", then check to see if the call to FindObjectOfType<AudioManager> returns the expected value.
You can break up your code:
var mgr = FindObjectOfType<AudioManager>();
if (mgr == null)
{
// some error here. Maybe throw an exception.
}
else
{
// Make sure (somehow) that it's really the object you wanted
}
mgr.Play("Woosh");
If the above tells you that you're getting the right object, then perhaps there's a problem playing the sound. Does Play throw any exceptions?
The point I'm trying to make here is that debugging involves questioning all of your assumptions, and doing tests to see where things are failing. This is best done with a debugger, because it lets you move through your code step-by-step to see exactly what's happening.
If you don't know how to use the debugger, now is the perfect time to learn. It will save you days of debugging.
It won't be making the sound because your collider is a trigger not a collision, so if you disable that box of Is Trigger, the sound will play when you collide with it.
If you want to pass through the collider like a trigger you need to use this functions.
void OnTriggerEnter(Collider other) {}
void OnTriggerStay(Collider other) {}
void OnTriggerExit(Collider other) {}
EDIT:
Also using FindObjectOfType is an expensive function, just make the Audio Manager a Singleton
Ok so I have this coroutine :
IEnumerator ShowCharTraits()
{
while(!hasPlayerChosen)
{
yield return null;
traitPanel.SetActive(true);
}
hasPlayerChosen = false;
traitPanel.SetActive(false);
// Debug.Log("Got called! job done");
}
It's being called like this from the awake method in my GameManager:
players = GameObject.FindGameObjectsWithTag("Player");
foreach (GameObject g in players)
{
ui_Controller.StartShowCharTraits();
g.GetComponent<PlayerToken>().isTurn = false;
}
StartShowCharTraits() is a simple method that does this :
public void StartShowCharTraits()
{
StartCoroutine("ShowCharTraits");
}
Now, I have checked the tags, no null reference exception, actually no errors or warnings are being thrown. If i load the scene in the editor and then play it everything works fine. traitPanel.SetActive(true); get called and my panel shows up. However when I load my scene from another scene using SceneManager.LoadScene(1); the above mentioned line is never reached. Any ideas why this is happening ?
Say you want to have one central place that is "like a singleton" in a Unity project. Example,
SoundEffects
LapTimer
Scores
SocialMediaConnections
All you do is this.
make your script SoundEffects.cs
recall that every Unity project must have a "preload" scene. (it's impossible to not have one)
in the preload scene, have a empty game object called "holder". make sure it is marked "DontDestroyOnLoad"
attach SoundEffects.cs to that holder
you're done.
there's just nothing more to it.
you're finished.
it's "just that simple"
So, any particular script, which happens to be attached to any particular object, in any particular scene, may need to access "SoundEffects"...
To do so, simply do this in Awake:
SoundEffects soundEffects = Object.FindObjectOfType<SoundEffects>();
then just use soundEffects anywhere in that script, for example soundEffects.PlayBoom(13) etc etc.
So in Awake()
Laps laps = Object.FindObjectOfType<Laps>();
Social social = Object.FindObjectOfType<Social>();
AI ai = Object.FindObjectOfType<AI>();
or whatever.
Unity is simple. Incredibly simple. It's just that easy.
write your script, LapTimer.cs or whatever
put it on a holder object in your preload scene (where else could it be?)
there's no "3", that's all there is. It's just that ridiculously simple.
Note that...
In the early days of Unity, someone unfortunately mentioned "singletons" on a QA board somewhere. (You can't have "singletons" in an ECS system, it's meaningless.) At the time, this led to huge discussions about how you can make a "singleton-like thingy" in Unity, which is piling confusion on confusion. Unfortunately from that day to this, you get people learning Unity, who see "singleton" mentioned here and there on the net, and it leads to endless confusion.
Once again, note that the idea of "managers" is just impossibly simple in Unity - explained above. It's trivial. It just couldn't be easier.
Note, above I mention you might want syntactic candy to avoid the incredible chore of typing Laps laps = Object.FindObjectOfType<Laps>();
There are very unfortunately no macros in Unity/c# so you need another way.
All you do is use a "Grid" script http://answers.unity3d.com/answers/1124859/view.html which has been popular for years in Unity.
But honestly, it's so simple to use Scores scores = Object.FindObjectOfType<Scores>(); that I usually just do that nowadays.
Ok how do I explain this. I have a singleton that acts as a data holder. While developing the scene with the game manager I had attached the singleton to the gamemanger object that hold a bunch of scripts. Now when I made the main menu I ofc added my singleton to it so I can carry info to the game scene and because of this my whole game manger object was being deleted. This is the culprit from the DataHolder :
void Awake()
{
if (instance == null)
instance = this;
else if (instance != this)
Destroy(gameObject);//This right here.
DontDestroyOnLoad(gameObject);
}
So I changed that line to Destroy(gameObject.GetComponent(instance.GetType()));
I'm making a 2D game using Monogame. My character loads to the game fine, however when a user presses the T key, I want my character to reload again (As if the character has teleported.)
I have loaded the player content in the LoadContent() function like so:
player.Load(Content);
And in the Draw() function, I have tried to reload the character again when 'T' is pressed by doing:
if (Keyboard.GetState().IsKeyDown(Keys.T))
{
player.Draw(spriteBatch);
}
and/or,
if (Keyboard.GetState().IsKeyDown(Keys.T))
{
player.Load(Content);
}
but neither of these seem to work.
My question is, what is the correct way to successfully load the character again and where do I place this if statement?
UPDATE:
Here's my player.Load() method used in the player class:
public void Load (ContentManager Content)
{
texture = Content.Load<Texture2D>("danPlayer");
}
You already have a function for loading the character, which is good. Now:
1) The Draw function is meant for drawing. It's running (kind of) async from the game. All your game logic should be in the Update function.
2) You have to know how to check if a keyboard button is clicked, not if it is being held down, because this way, the character will Load as long as you are holding the 'T' key. No matter how fast you click 'T' key, the way you are doing it, you'll fire the Load function at least 2-3 times.
3) To check this, you'll need 2 variables holding the current and the last state of keyboard, and all your game logic should be in between the updates for new and old keyboard state. You could also create a simple function to check for Click(Keys key) and Hold(Keys key)
KeyboardSate ksNew, ksOld;
protected override void Update (GameTime gameTime)
{
ksNew = KeyboardSate.GetState();
*your logic here*
if (Click(Keys.T))
player.Load(Content);
ksOld = ksNew;
}
private bool Click(Keys key)
{
return ksNew.IsKeyDown(key) && ksOld.IsKeyUp(key);
}
private bool Hold(Keys key)
{
return ksNew.IsKeyDown(key);
}
If you're using XNA the usual way, you don't want to call Load to reload the player. That function is usually used only to load the player's assets, primarily images.
Somewhere in your code there should be some method where you're initializing aspects of your player, such as position and perhaps some sort of state. What you do is you want to call that code whenever T is pressed.
I would refer to Monset's answer about how to go about getting your keypress. I would also recommend picking up a book on XNA, since most of your questions that I've seen on SO have been about things that are often covered in such books. The one I initially learned from was Learning XNA, which seems like it might be your speed.
I have a seperate script of time which I use to show time in my scene. It contains hour and minute and seconds variable.I want to do some specified work e.g., code execution on specified time and currently i am doing something like this. in Update. I am running a function which check continously check time variable in order to run an animation.
void Update()
{
checkTrainArriveTime();
}
void checkTrainArriveTime()
{
if (timeManager.GetComponent<Scale2>().hour == trainArriveTimeHour && timeManager.GetComponent<Scale2>().min == trainArriveTimeMin
&& isTrainArriveConditionExecute)
{
isTrainArriveConditionExecute = false;
PlayAnimationClip("Start");
}
else if (timeManager.GetComponent<Scale2>().min != trainArriveTimeMin)
{
isTrainArriveConditionExecute = true;
}
}
As Time will match this function will play the animation. Now I have 50 script attached to 50 different game Object. It is
working fine but It definitely not the right way to use Update Event. In my code, It is necessary to check time on every frame and
extra load on update. Is there any efficient way to do this Job?.
I can see your struggle. You are right, it is definitely not the best way forward.
The best option I can see here would be creating Animation Manager which is a singleton instance (there is only one instance allowed per application).
I would suggest moving your animation triggering logic to an Update method of AnimationManager.
Once you have done that. You will be able to access its instance calling AnimationManager.getInstance() method.
Next step is creating internal registry that would be nothing else than just a list of your registered game objects that you want to trigger animation for.
I don't know what exactly is your timeManager but I can imagine it is probably an instance of TimeManager controller that you drag and drop onto your public timeManager property. Consider turning it into singleton as well or at least moving assignment of timeManager.GetComponent<Scale2>() into Awake() method.
It is important to not to call GetComponent() method from inside of Update()', as it has an impact on performance.GetComponent` is quite expensive to call.
Hope it helps.
I'm trying to play a single animation once on a character in Unity using MecAnim. I'm really not familiar with any of this, so I might have got the approach completely wrong.
The base layer contains a lot of walking stuff, but the animation I'm trying to trigger is on a seperate Right Arm layer with an empty Default state. There is a Wave trigger applied to the Default -> Wave transition, and a timed exit on the Wave -> Default one.
I then have a script which (while debugging) fires when you press 'O' and causes the character to wave, once.
if (Input.GetKey(KeyCode.O))
{
AnimatorStateInfo currentState = this.animator.GetCurrentAnimatorStateInfo(1);
if (currentState.IsName("Right Arm.Default"))
{
Debug.Log("Waving triggered");
this.animator.SetTrigger("Wave");
}
}
The animation plays as expected, but the console shows that the IsName check is always passing as true, which makes me think I'm doing something wrong.
The in transition takes a fraction of a second to complete, then after that if the SetTrigger method is invoked again, the animation will play twice.
I'm looking for a simple way of ensure the trigger is only ever set when the state of this layer has fully returned to default.