Make your first 2D Game "The main game scene" section. Mobs do not spawn in c#

Godot Version

v4.2.2.stable.mono.official[15073afe3]

Question

I followed this tutorial, but the mobs don’t appear.

This is the project file.

Player:


public partial class Player : Area2D
{
	[Signal]
	public delegate void HitEventHandler();
	[Export]
	public int Speed { get; set; } = 400; // How fast the player will move (pixel/sec).
	public Vector2 ScreenSize; // Size of the game window.

	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		ScreenSize = GetViewportRect().Size;
		Hide();
	}

	// Called every frame. 'delta' is the elapsed time since the previous frame.
	public override void _Process(double delta)
	{
		var velocity = Vector2.Zero; // The player's movement vector.

		if (Input.IsActionPressed("move_right"))
		{
			velocity.X += 1;
		}

		if (Input.IsActionPressed("move_left"))
		{
			velocity.X -= 1;
		}

		if (Input.IsActionPressed("move_down"))
		{
			velocity.Y += 1;
		}

		if (Input.IsActionPressed("move_up"))
		{
			velocity.Y -= 1;
		}

		var animatedSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");

		if (velocity.Length() > 0)
		{
			velocity = velocity.Normalized() * Speed;
			animatedSprite2D.Play();
		}
		else
		{
			animatedSprite2D.Stop();
		}

		Position += velocity * (float)delta;
		Position = new Vector2(
			x: Mathf.Clamp(Position.X, 0, ScreenSize.X),
			y: Mathf.Clamp(Position.Y, 0, ScreenSize.Y)
		);

		if (velocity.X != 0)
		{
			animatedSprite2D.Animation = "walk";
			animatedSprite2D.FlipV = false;
			animatedSprite2D.FlipH = velocity.X < 0;
		}
		else if (velocity.Y != 0)
		{
			animatedSprite2D.Animation = "up";
			animatedSprite2D.FlipV = velocity.Y > 0;
		}
	}
	private void OnBodyEntered(Node2D body)
	{
		Hide();// Player disappears after being hit.
		EmitSignal(SignalName.Hit);
		// Must be deferred as we can't change physics properties on a physics callback.
		GetNode<CollisionShape2D>(nameof(CollisionShape2D)).SetDeferred(CollisionShape2D.PropertyName.Disabled, true);
	}
	public void Start(Vector2 position)
	{
		Position = position;
		Show();
		GetNode<CollisionShape2D>(nameof(CollisionShape2D)).Disabled = false;
	}
}

Mob:

using Godot;

public partial class Mob : RigidBody2D
{
	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		var animatedSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
		string[] mobTypes = animatedSprite2D.SpriteFrames.GetAnimationNames();
		animatedSprite2D.Play(mobTypes[GD.Randi() % mobTypes.Length]);
	}

	// Called every frame. 'delta' is the elapsed time since the previous frame.
	public override void _Process(double delta)
	{
	}
	private void OnVisibleOnScreenNotifier2DScreenExited()
	{
		QueueFree();
	}
}

Main:


using Godot;

public partial class Main : Node
{
	[Export]
	public PackedScene MobScene { get; set; }
	private int _score;
	public void GameOver()
	{
		GetNode<Timer>("MobTimer").Stop();
		GetNode<Timer>("ScoreTimer").Stop();
	}

	public void NewGame()
	{
		_score = 0;

		var player = GetNode<Player>("Player");
		var startPosition = GetNode<Marker2D>("StartPosition");
		player.Start(startPosition.Position);

		GetNode<Timer>("StartTimer").Start();
	}

	private void OnScoreTimerTimeout()
	{
		_score++;
	}

	private void OnStartTimerTimeout()
	{
		GetNode<Timer>("MobTimer").Start();
		GetNode<Timer>("ScoreTimer").Start();
	}
	private void OnMobTimerTimeout()
	{
		// Note: Normally it is best to use explicit types rather than the `var`
		// keyword. However, var is acceptable to use here because the types are
		// obviously Mob and PathFollow2D, since they appear later on the line.

		// Create a new instance of the Mob scene.
		Mob mob = MobScene.Instantiate<Mob>();

		// Choose a random location on Path2D.
		var mobSpawnLocation = GetNode<PathFollow2D>("MobPath/MobSpawnLocation");
		mobSpawnLocation.ProgressRatio = GD.Randf();

		// Set the mob's direction perpendicular to the path direction.
		float direction = mobSpawnLocation.Rotation + Mathf.Pi / 2;

		// Set the mob's position to a random location.
		mob.Position = mobSpawnLocation.Position;

		// Add some randomness to the direction.
		direction += (float)GD.RandRange(-Mathf.Pi / 4, Mathf.Pi / 4);
		mob.Rotation = direction;

		// Choose the velocity.
		var velocity = new Vector2((float)GD.RandRange(150.0, 250.0), 0);
		mob.LinearVelocity = velocity.Rotated(direction);

		// Spawn the mob by adding it to the Main scene.
		AddChild(mob);
	}
	// Called when the node enters the scene tree for the first time.
	public override void _Ready()
	{
		NewGame();
	}

	// Called every frame. 'delta' is the elapsed time since the previous frame.
	public override void _Process(double delta)
	{
	}
}

Can someone guide me?

1 Like

This function spawns your mob, but at least in this code it is never referenced. I.e it is never used.

1 Like

this is timeout signal of Timer for MobTimer

1 Like

use GD.Pritnt to check parts of your code.
you can easily check what part of your code execute and what not.
check if your mob Enter tree.

In Main script line: GetNode<Timer>("StartTimer").Start();runs, but don`t start. I dont know why.



1 Like

With the help of artificial intelligence, I got a code that connected with a little change (I think it was for the old version!) of the signals! I think it is called signal connection through code (I don’t know). But I have to do this for all the signals!
The problem that exists is that the signals are not connected as described in the tutorial.
But it is done through this code!

As you can see in the picture, because I haven’t changed the code of the OnBodyEntered signal yet, nothing happens when the mobs hit the player, and probably the OnVisibleOnScreenNotifier2DScreenExited signal doesn’t work (unfortunately, because it’s not visible in the game editor! Maybe I don’t know. Anyway I just want to learn.)

main script:

	public void GameOver()
	{
		var mt = GetNode<Timer>("MobTimer");
		mt.Connect("timeout", new Callable(this, "GameOver"));
		mt.Stop();
		var st = GetNode<Timer>("ScoreTimer");
		st.Connect("timeout", new Callable(this, "GameOver"));
		st.Stop();
	}

	public void NewGame()
	{
		_score = 0;

		var player = GetNode<Player>("Player");
		var startPosition = GetNode<Marker2D>("StartPosition");
		player.Start(startPosition.Position);

		//GetNode<Timer>("StartTimer").Start();
		var startTimer = GetNode<Timer>("StartTimer");
		startTimer.Connect("timeout", new Callable(this, "OnStartTimerTimeout"));
		startTimer.Start();
	}

	private void OnScoreTimerTimeout()
	{
		_score++;
	}

	private void OnStartTimerTimeout()
	{
		var mobTimer = GetNode<Timer>("MobTimer");
		mobTimer.Connect("timeout", new Callable(this, "OnMobTimerTimeout"));
		mobTimer.Start();
		var scoreTimer = GetNode<Timer>("ScoreTimer");
		scoreTimer.Connect("timeout", new Callable(this, "OnScoreTimerTimeout"));
		scoreTimer.Start();
	}

1 Like

If you move your nodes around in the scene tree it will break signals in the inspector unfortunately.

Can you explain more clearly?
What I did was that I first wrote the signal methods, then I connected the signals to it.

It’s not really important, but…

What I’m describing is if you creat a signal via the inspector. And define a function _on_node_exaple_signal. Then move that node the signal is pointing too in the scene tree.(Up or down a parent/child) then it is likely that the signal has the wrong path to the node with the method. And it will fail to connect and will fail silently.

This is one possibility. But you fixed it that is all that matters.

1 Like

What is the usual way to fix this problem? Because coding like this is really annoying.

It’s either in the code as you did or via the inspector. Just be aware that if you move node’s ancestry you will also have to update the signal connection in the inspector.

1 Like

For this, should I disconnect the signals in the inspector first and then connect them again?
In other words it should do this for all signals in the scene tree?

Yes, but only signals to the node that moved.

1 Like

I disconnected and connected the signal of all the nodes, it didn’t work, I even deleted the StartTimer node and created it again and connected the signal, it still didn’t work. Now I want to delete the whole scene tree and rebuild it to see if it works or not.

The place of a perfect and free game engine for C# language is felt.

Interesting, I rebuilt the main scene tree and it still doesn’t work.

You shouldn’t need to rebuild the scene. Do you follow this procedure? And is there a green square with an arrow pointing to your connected function?

1 Like

You connected to the wrong signal, you need to use the C# naming, this was already pointed out on the documentation issue you opened some time ago, this is resolved, you can see in your screenshot above, and as pointed out above this is in the documentation in several places