#I cant push the enemy because its following me
1 messages Β· Page 1 of 1 (latest)
Test this while in play mode.
Set your inspector to Debug mode.
Pause when the enemy script should be disabled.
Select the enemy object, and see if the script is indeed disabled, via the inspector.
it is not disabling as I can see
Alright, before proceeding, there is a minor naming convention issue.
private GameObject GameObject;
//..
public GameObject gameobject;
That practice is very discouraged, as it easily leads to confusion.
While it is possible, it is rarely practical, especially when others read it.
This would be better:
private GameObject _other;
//..
public GameObject Other;
Now, inside ScriptFalse()
private void ScriptFalse()
{
Other.GetComponent<EnemyMovement>().enabled = false;
Debug.Log("Disabled EnemyMovement for: " + Other.name); // Add this <--
}
are you correcting or should I add one more
Correcting your existing variable names.
I have only one here
There are two scripts.
okay I did things you said playing
I did it like that but I wrote wrong
Its like blinking
if (physicsPickupScript != null && physicsPickupScript.hold == true)
{
ScriptFalse();
if (Input.GetKeyDown(KeyCode.F))
{
ScriptFalse();
}
}```
I changed this like that
just tried
still enables it even when hold is true
Its getting enabled and disabled repeatedly while I am holding
Ah yes, I see why
private void Update()
{
if (physicsPickupScript != null && physicsPickupScript.hold == true)
{
ScriptFalse(); // <--- This will always run when Hold == true.
if (Input.GetKeyDown(KeyCode.F))
{
Invoke("ScriptTrue", 2);
}
}
if (physicsPickupScript != null && physicsPickupScript.hold == false)
{
Invoke("ScriptTrue",2);
}
}
@trail sorrel
wait, that's not right
(it's late here)
Can you show the pickup script?
PS: It's beyond midnight here, and if you don't respond fast, I'll probably get distracted.
Alright.
Since the SomeScript is depending on another script's hold property, things could get difficult to debug.
Instead of polling with complex if-chains, it's possible to simplify this by calling Public methods on other scripts, ultimately allowing them to control themselves, instead of each other.
Before I continue, are you following a tutorial?
@trail sorrel
if you're done with the tutorial, that is fine
if not, it might cause trouble later
I'm making assumptions of how your game works, so correct me if I'm wrong.
In your PhysicsPickup script you have a Raycast.
In that raycast (if it hits), you can do hit.gameObject.TryGetComponent<EnemyMovement>(out EnemyMovement enemy)
which line do you mean 55?
there is only one Raycast in the entire script
yes
Then you can make a Public method in the EnemyMovement script
and use that if the raycast hits.
should I add hit.gameObject.TryGetComponent<EnemyMovement>(out EnemyMovement enemy) under if or in the pharanthesis
if (Raycast(...)) // True when hitting something.
{
// This only happens if the Raycast hits a collider.
}
inside the { brackets }
But I still need to add the hold variable I think
because shouldnt it disable the script when I look at the enemy
There are more things to do, yes.
If I understand your code correctly,
void grav() should reverse the process
Though it's a bit tricky to follow what happens.
you mean like = !usegravity
I mean the method itself
grav()
btw all methods should be PascalCase()
private fields should be _camelCase prefixed with
I didnt know it thanks
where do I get the name hit
local variables (inside methods) have camelCasing without _
RaycastHit hit is normal convention
While yours is named different, I made the example with simply hit
what is mine called if you can see , I dont know much about this raycast thing
did you not write this code yourself?
I took it from the tutorial
link the tutorial
Are the variable names from the tutorial?
In this video, I go over how you can make a physics pickup system in Unity in 3 Minutes.
Join my Discord! βΊ https://discord.gg/jrRJgfBz2y
#UnityTutorials #Unity3D
-= Music =-
Music: Dopamine
Artist : DZGRIMX
link to the video: https://youtu.be/3S6uyNk9HpY
channel: https://www.youtube.com/channel/UCB...
probably some of them
I dont remember if I changed some of them
I just watched half. It's from the tutorial.
I'm sure it gets the desired result, but that tutorial has no idea of C# naming convention, which makes it a bad tutorial.
There's enough to learn, so we don't need confusion π
I have no idea too, thats the reason I didnt notice π
So anyway, the tutorial assumes people know how Raycasts work.
Raycast has a number of different overloads.
https://docs.unity3d.com/ScriptReference/Physics.Raycast.html
out RaycastHit is in some of them.
out means it's not an argument the method absorbs, but an argument to which a result is given.
RaycastHit contains information about the hit
RaycastHit hit;
if(Raycast(..., out hit, ...))
{
Debug.Log(hit.gameObject.name)
}
Can also be written like this, to avoid an unnecessary line of code.
if(Raycast(..., out RaycastHit hit, ...))
{
Debug.Log(hit.gameObject.name)
}
so if the Raycast actually hits something, everything in the if-statement will run
hit doesn't exist outside the if-statement, unless you declare it, like the first example
if (Physics.Raycast(CameraRay, out hit, PickupRange, PickupMask))
{
hit.gameObject.TryGetComponent<EnemyMovement>(out EnemyMovement enemy);
CurrentObject = hit.rigidbody;
CurrentObject.useGravity = false;
}``` I get 'RaycastHit' does not contain a definition for 'gameObject' and no accessible extension method 'gameObject' accepting a first argument of type 'RaycastHit' could be found (are you missing a using directive or an assembly reference?) on the line starting with hit
is it because I havent got a gameObject
Minor mistake from my side.
RaycastHit doesn't directly reference gameObject.
But you can get it from either collider, rigidbody or transform
π
name also exists on all those, so gameObject isn't necessary
Next:
private Rigidbody CurrentObject;
should be changed into
private EnemyMovement CurrentObject;
if (Physics.Raycast(CameraRay, out hit, PickupRange, PickupMask))
{
if (hold == true)
{
hit.rigidbody.TryGetComponent<EnemyMovement>(out EnemyMovement enemy);
}``` its like that now
EnemyMovement should implement a public method named GetPickedUp()
And a method named GetDropped()
run CurrentObject.GetPickedUp() from the raycast if-statement
run CurrentObject.GetDropped() from grav()
I should get the disabling line under hit.rigidbody.TryGetComponent<EnemyMovement>(out EnemyMovement enemy);
right
not sure about 'disabling line', but I'll make it clear in a sec
if (Raycast...)
{
hit.rigidbody.TryGetComponent<EnemyMovement>(out EnemyMovement enemy);
if (enemy)
{
enemy.GetPickedUp();
CurrentObject = enemy;
}
}
is enemy here gameobjects name
it's the same gameobject, but different component
While this current code example uses the EnemyMovement component,
that is going to be changed last, so that any type of script can be picked up, if implemented.
is there something about the enemys name in hierarchy there?
What do you mean?
like I got 2 enemies
one of them is Enemy other one is Enemy (1)
their names mean nothing in this code
the only reason we used Debug.Log(name) earlier, was to confirm which object was being hit
hit.rigidbody.TryGetComponent<EnemyMovement>(out EnemyMovement enemy);
Cannot implicitly convert type 'EnemyMovement' to 'UnityEngine.Rigidbody'
what do you reccomend after hit in first line here
okay trying
if (enemy)
{
enemy.GetPickedUp();
CurrentObject = enemy;
}
}
CurrentObject = hit.transform;
CurrentObject.useGravity = false;```
is this true?
Cannot implicitly convert type 'UnityEngine.Transform' to 'UnityEngine.Rigidbody'
Cannot implicitly convert type 'EnemyMovement' to 'UnityEngine.Rigidbody'
No. This is:
if (Raycast...)
{
hit.transform.gameObject.TryGetComponent<EnemyMovement>(out EnemyMovement enemy);
if (enemy)
{
enemy.GetPickedUp();
CurrentObject = enemy;
}
}
useGravity will be moved to the EnemyMovement script, inside GetPickedUp() and GetDropped()
okay
CurrentObject = hit.transform;
CurrentObject.useGravity = false;
so deleting these
yes, it should look exactly like what I posted, minus the simplified raycast
Cannot implicitly convert type 'EnemyMovement' to 'UnityEngine.Rigidbody'
If you don't want to delete, you can
Comment by selecting the lines and pressing CTRL + K + C
Uncomment by CTRL + K + U
CurrentObject = enemy;
go to the declaration of CurrentObject, and change its type from Rigidbody to EnemyMovement
This is the declaration:
private Rigidbody CurrentObject;
okay I changed it get some errors
about functions
Getpickedup ,usegravity ,velocity and position
Hm. I didn't consider the FixedUpdate, sec
go to the EnemyMovement script and create the public methods GetPickedUp() and GetDropped()
okay I did
Hm, I'm not sure I have the brainpower to work through this obstacle right now.
But we can work around it.
My idea was to limit direct control to each class for itself, and keep other interactions via methods.
You can probably fix that later, but it might Not be necessary.
So to work around it, change CurrentObject back to Rigidbody
and I'll rewrite the Raycast
below the declaration of CurrentObject
make a private EnemyMovement CurrentMovement;
if (Raycast...)
{
hit.transform.gameObject.TryGetComponent<EnemyMovement>(out EnemyMovement enemy);
if (enemy)
{
enemy.GetPickedUp();
CurrentObject = hit.rigidbody;
CurrentMovement = enemy;
}
}
I got no errors now
Good.
Then add
CurrentMovement.GetDropped();
into grav()
Getdropped and getpickedup are still empty
as well as
CurrentMovement = null;
to grav?
π
In EnemyMovement add a private bool _canMove = true;
In GetPickedUp() set _canMove = false;
in GetDropped() set _canMove = true;
in FixedUpdate() add this line at the top:
if (_canMove == false) return;
if that statement is true, the rest of FixedUpdate won't be run, because it returns early
thus cancelling the movement
just as effectively as disabling the script should
but more flexible, as we can still do things with the script
The name '_canMove' does not exist in the current context
The first instruction up there
In EnemyMovement add a private bool _canMove = true;
that makes sense
I did
post the full script
enemy movemnt
yes
I got this one
Assets\PhysicsPickup.cs(113,13): error CS0103: The name '_canMove' does not exist in the current context
if (_canMove == false) return;
ohh
it shouldn't be in that script
That line is supposed to be inside EnemyMovement.FixedUpdate()
I forgot about fixed update in enemymovement
Good stuff.
Try it out now, and see if it all works or not.
Hm. Post your Pickup script.
so when you throw the enemy, it doesn't follow the player anymore
but it was moving before picking it up?
yeah
Did this somehow work before my changes?
like I couldnt throw just dropping it
and it was following
hm ok
Yeah, that one was 100% replaced by these changes.
inside GetDropped()
try setting velocity to Vector3.zero
might not work, due to the throwing.
I'm not sure what comes first.
actually, now I realize
kinematic
hm, maybe that's not it
should I try this
yes, do try and see if it helps
like rigidbody.velocity
yes
{
_canMove = true;
rb.velocity = new Vector3(0, 0, 0);
}```
Vector3.zero also works
after I throw
when I press e one more time
enemy starts moving
maybe thats because somescript I didnt remove it
no it isnt
_canmove gets false but doesnt get true till I pick enemy
up
that's the opposite of what should happen π€
code looks good on my side.
Did you update anything?
add a Debug.Log() to GetDropped()
add one to GetPickedUp() as well
when I drop enemy it resumes following
but when I throw it doesnt
it calls get dropped function only when I press e
thats the reason I think
Sounds good. Let's stare at some code.
Line 99 in PhysicsPickup
I need a few moments to come up with how this should be.
The enemy needs a GetThrown() method
If you are tired we can continue later you said it is midnight
In the EnemyMovement script, on the top or bottom, add the following:
private enum EnemyState
{
Moving,
NotMoving,
Thrown
}
Do you know about enums?
I have never heard about them
I'm on fire right now, and the end is near in sight.
enums are numbers disguised as text
private enum EnemyState
{
Moving = 1,
NotMoving = 2,
Thrown = 3
}
oh okay
they can have any number
the numbers are mostly important for other things
Since the Enemy will have more than two states,
it will be much more easy to keep track of the code logic, if we use a switch (aka state machine)
private EnemyState _state;
private void FixedUpdate()
{
switch (_state)// Autocomplete here, and it should create the entire switch
}
do Not delete anything yet
which script
EnemyMovement
ok
in case it doesn't autocomplete, like it didn't for me just now
switch (_state)
{
case EnemyState.Moving:
// All the existing FixedUpdate() code goes here, minus the first line that returns
break;
case EnemyState.NotMoving:
break;
case EnemyState.Thrown:
break;
}
However
instead of placing the code directly in the switch
make a method
and call it from the switch
(much more readable)
The type or namespace name 'EnemyState' could not be found (are you missing a using directive or an assembly reference?)
I got this error
private enum EnemyState _state;
and you need to create the enum
private enum EnemyState
{
Moving = 1,
NotMoving = 2,
Thrown = 3
}
Note: EnemyState might be a bad word. It could be InternalState.
okay got rid of it
no problem
should it be _state
the variable name, yes
This was supposed to be declared:
private EnemyState _state;
I thout we made something wrong about a line but it was a different line
now codes okay
Good. Have you moved all the code from FixedUpdate() into a separate method?
(minus the switch)
// All the existing FixedUpdate() code goes here, minus the first line that returns
I put them here
where should I put the first line that returns
switch (_state)
{
case EnemyState.Moving:
Moving();
break;
case EnemyState.NotMoving:
NotMoving();
break;
case EnemyState.Thrown:
BeingThrown();
break;
}
delete or comment it out
this switch replaces its purpose
okay I deleted
Now, make sure all the movement code is in Moving() or some other named method.
Make NotMoving() even though it's more like DoNothing() right now.
And BeingThrown() is going to have the new code I'll provide
you mean following right by movement code
well, yes, that is a good distinction
just make an empty method, in case you ever need to add some code there
okay
those methods should be Private
Now, add a bool, or rename the previous one we used to: _isFalling
do you mean canmove by previous bool
then make void OnCollisionEnter
(it should auto-complete the rest)
Inside that, we need to detect when the Enemy should start following again.
Usually by detecting that something in the Ground layer was hit.
Does your game have a Ground layer?
its in ui layer
didnt autocomplete
you're using the UI Layer as the Ground layer?
I dont remember why i did it
Probably best to dedicate a separate layer, or use Default layer
UI as ground is confusing π
Does this appear to you?
ok
ANyway, keep UI as ground for now
To reduce the amount of chaos
I take it the Ground objects are already in the UI Layer?
ok
Are you familiar with LayerMask?
public LayerMask _groundLayer; // Choose layer(s) in the inspector.
I used once
it's essentially a sequence of bits (00110101)
an On/Off bit for each of the 32 layers in Unity
It can have none, all, or any combination of layers.
Add that variable to your script.
added and chose ui in inspector
Then, inside OnCollisionEnter()
if (collision.collider.gameObject.layer == _groundLayer)
{
_isFalling = false; // Declare this also, in the script (private bool)
}
okay
then, add _isFalling to the if-statement
if (_isFalling && collision.collider.gameObject.layer == _groundLayer)
Now, in GetThrown() add
_isFalling = true;
Finally, in the three Public methods:
// GetPickedUp()
_state = EnemyState.NotMoving;
// GetDropped()
_state = EnemyState.Moving;
// GetThrown()
_state = EnemyState.BeingThrown;
_isFalling = true; // already added
and in Awake() set _state = EnemyState.Moving
After this, test \o/
are all the scripts Saved?
yes
Post them.
check the PickupMask
its on pickup layer
and the enemy is also?
yes
Disable the EnemyMovement component of one enemy.
See if that one can be picked up.
it can be done while in Play mode
no I cant
pick it up
Any errors?
Ok. Let me go through my instructions and your scripts.
Man I have to sleep can you please write if you see something I dont know how to thank you for your help
Alright. I'll see what I can find.
You're welcome o/
Looks like you removed an ending bracket.
FixedUpdate() is inside void Update() in Pickup
Add } before FixedUpdate
and remove the last } at the bottom of the script
then CTRL + K + D to auto-correct indentation (for entire script)
Let me know when it works o/
Once the script is complete, there is one last milestone.
This current solution will work for EnemyMovement, but we can make it work for any script we want.
By declaring an interface, these can be implemented by any other class.
public interface IInteractable
{
public void GetPickedUp();
public void GetDropped();
public void GetThrown();
}
Like so:
public class EnemyMovement : MonoBehaviour, IInteractable // <-- Add IInteractable in declaration
And be used like so:
private IInteractable CurrentMovement;
//..
hit.transform.gameObject.TryGetComponent<IInteractable>(out IInteractable enemy);
And however the class implements the behavior, is up to the class itself.
everything is good but when I throw them or drop them they dont follow me
maybe thats because they are lying down and cant stand up
I freezed the rotation of enemys and tried
but that didnt work
{
case EnemyState.Moving:
Moving();
break;
case EnemyState.NotMoving:
break;
case EnemyState.Thrown:
GetThrown();
break;
}```should I delete GetThrown(); here
Alright. Post your updated scripts.
GetThrown should be Public, and is indeed not supposed to be in the switch.
It is accessed only via the Pickup script
Being a bit distracted here btw, but starting to look now.
Ah yes, looks like I didn't do everything with _isFalling and the State
might not need the bool
in EnemyMovement
copy the switch from FixedUpdate, into OnCollisionEnter
Remove the Moving() method from it.
Move the existing code in OnCollisionEnter into the Thrown state
Then replace this
_isFalling = false; // Declare this also, in the script (private bool)
With that
_state = EnemyState.Moving;
That might be all.
_isFalling can then be completely removed.
I expect that will work, but if not, then I have time right now, or in 7 hrs.
private void OnCollisionEnter(Collision collision)
{
switch (_state)
{
case EnemyState.Moving:
break;
case EnemyState.NotMoving:
break;
case EnemyState.Thrown:
if (collision.collider.gameObject.layer == _groundLayer)
{
_state = EnemyState.Moving;
}
break;
}
}
Optional:
Instead of setting the state directly to EnemyState.Moving,
you could make a new state EnemyState.Recovering
to add a delay from when it hits the ground, to when it starts moving.
should I delete the switch in fixedupdate
no
that remains just as it was
the switch is so other code can also be state-specific
It's a good way to control complex code.
bool _canMove can be removed
is your game 3D or 2D?
3d
and the objects have Rigidbody, not Rigidbody2D?
that's fine. Only one of the colliders needs a rigidbody, for a collision event to occur
enemys have
as they should, for movement
Screenshot the object hierarchy, while selecting the object with the EnemyMovement script
I changed something
Tell me which object has the Collider
enemies have capsule, floor have box collider
player has box too
ok. Screenshot the Enemy prefab hierarchy
components?
only enemy is contained in enemys hierarchy
ah so it's only one object. simple, ok.
Have you changed anything since yesterday, which I did not specify?
{
CurrentObject = hit.rigidbody;
CurrentObject.useGravity = false;
}
else
{
hit.transform.gameObject.TryGetComponent<EnemyMovement>(out EnemyMovement enemy);
if (enemy)
{
enemy.GetPickedUp();
CurrentObject = hit.rigidbody;
CurrentMovement = enemy;
}
}```
I only changed this
it detects collision
I saw I wrote debug.log to wrong place
Ok. Post your scripts again.
Is the enemy rotation 0,0,0 by default?
yes
inside OnCollisionEnter()
in the Thrown > if groundlayer
Add another line below _state = EnemyState.Moving;
rb.rotation = Quaternion.Euler(0,0,0);
@trail sorrel
Note that when using Rigidbody for movement, we should not use Transform to move/rotate, because that would be bypassing the physics system
Use Inspector Debug mode, to see the Enemy's state while testing
I played but didnt see anything change
Request:
Screenshot the scene, when the enemy has landed, but is not moving.
Have the Enemy object selected, so I can see all the script's values via the inspector.
inside GetThrown()
add Debug.Break()
not pause, but break*
it will Pause the editor
in the code you shared, it seems you haven't added GetThrown() in the Pickup script
yeah I just checked it and yeah
should be placed below Line 107 somewhere
how should write it tried enemy.Getthrown
enemy.GetThrown()
current.getthrown gets no errors
:D
alright, remove the Debug.Break() (or comment out)
or just, for now, Unpause the Editor
so you can see the rest
but now It just stays on thrown
Does it log the ground when hitting it?
private void OnCollisionEnter(Collision collision)
{
Debug.Log(gameObject.name);
switch (_state)
{
case EnemyState.Moving:
break;
case EnemyState.NotMoving:
break;
case EnemyState.Thrown:
if (collision.collider.gameObject.layer == _groundLayer)
{
Debug.Log("Collided with Ground"); // <-- Add this.
_state = EnemyState.Moving;
}
break;
}
}
add that debug log
added
check the inspector
is ground layermask still good?
oh wait
My mistake. Layer index != LayerMask
I have to be somewhere like around past 50 I should close pc a few minutes later
which line
public static bool HasLayerInMask(LayerMask layerMask, int layerIndex)
{
return (layerMask & (1 << layerIndex)) != 0;
}
sec
add that method to your Pickup script
if (HasLayerInMask(_groundLayer, collision.collider.gameObject.layer))
{
Debug.Log("Collided with Ground");
_state = EnemyState.Moving;
}
where am I adding this
π
LayerMask uses a sequence of bits internally, so the integer it represents, is not the same as a Layer's index, but the sum of all bits/layers enabled in the mask π€¦ββοΈ
Anyway.
You can read more about LayerMask at a later time.
https://github.com/Rovsau/LayerMask-Explained
bro it worked thank you so much
Awesome!
π
There are 2-3 small things I wish to do before we part ways.
Now that your code works, you should look into simplifying it, so you can understand it later.
We're not doing that right now. You can mention me in Official for that purpose later.
The pickup script has a lot of nesting
if (true)
if (true)
if (true)...etc
Nesting is not good for humans to read, so we usually nest max 3 times, and reduce everything to methods.
If you are able to do that, it's much more maintainable.
And to improve it even better, you could make the Pickup script have States as well.
Not that I think you want to do that after all this π
But it's a thought.
Not in this script but i will use your advices for my future scripts thanks
Next: Rewrite Pickup script to work with IInteractable instead of the EnemyMovement class itself.
The interface needs to be declared outside the class itself, so it is accessible to other classes.
Usually, an interface has its own file.
I will take a look at interfaces it was the first time I heard them when you said
And lastly, your Pickup script is directly modifying the EnemyMovement script, instead of using methods of the Enemy itself.
It works now, so no reason to care, but in the future, try to let each script run its own mechanics, at outside request.
That is all.
GOOD WORK
Have a nice day 