How to make a pokeball effect in godot?

Godot Version

4.3-stable version

Question

I’m trying to create a Pokéball effect in Godot using RigidBody3D. When it collides, I want it to bounce up, and a ray (GPUParticles3D) should appear at the monster’s position and move towards the Pokéball. I’m using apply_central_impulse to make the Pokéball go up when it collides with the monster, but it’s not working properly. I need it to freeze in the air, and the ray should appear at the monster’s position and move towards the Pokéball. Does anyone have any ideas on how to achieve this? Thank you very much guys

1 Like

What script do you have so far? What’s “not working”? what did you expect to happen vs what really happened?

So, in my game, my pokeball is a card and in my game i did something like this:
My node organization of the card:

image

Area Script:

func _on_area_entered(area): #Area collision that trigger when the card collides with the enemy
	if area is Area3D and area.get_parent() is Enemy:
		_capture_enemy(area.get_parent())
	pass

Catch script:

func _capture_enemy(enemy: Enemy): #catch script
	print("collided enemy")
	randomize()
	enemy.hide() #Hide the enemy that collided with the card
	enemy.set_physics_process(false) #Unable him to move
	enemy_ref = enemy #Get the reference of enemy, enemy_ref is a global variable
	animation_player.play("catch_enemy") #Play the catch animation
	pass

Basically the catch_enemy animation will trigger three functions:

func apply_card_force(): #function that applies the force to the card goes up
	apply_central_impulse(transform.basis * Vector3(0, FORCE_BACKCARD, 0)) #FORCE_BACKCARD is the force that will be applied to the card i set it 5 to not flight away to much
	
func freeze_in_air(): #A function that freezes the card in the air
	linear_velocity = Vector3.ZERO
	angular_velocity = Vector3.ZERO
	gravity_scale = 0.0

func play_catch_particle()->void: #This function basically instantiate the ray(gpupartcle3D) that will be appears in the enemy position and will go to the card position in air
	var instance:Object = preload("res://scenes/magic/catch_particle.tscn")
	var instance_energy:CatchParticle = instance.instantiate()
	instance_energy.position = enemy_ref.global_position #Instantiate the catch_particle in the enemy position
	instance_energy.transform.basis = enemy_ref.global_transform.basis
	instance_energy.set_target(global_position) #Set the target that ray will pursue
	get_parent().add_child(instance_energy) #Add to the scene

The CatchParticle script:

func _process(delta):
	if card_pos != Vector3.ZERO: #Trigger when card_pos variable receives the position of the card		
        await get_tree().create_timer(2) #Wait 2 secs to not move too fast
		position += transform.basis * card_pos * delta #Alter the position of particle to the direction of the card
	pass
	
func set_target(pos: Vector3): #Set the card position
	card_pos = pos #card_pos is a global variable
	print(card_pos)
	pass

func _on_body_entered(body: Node3D) -> void: #Collide script
	if body.get_parent() is CardPhysicItem:  #When the ray collides with the card check the probability function of the card to catch or not the monster
		var card : CardPhysicItem = body.get_parent() as CardPhysicItem
		card.check_catch_probability()
		queue_free() #Free the ray from the scene when it collides with the card
	pass 

The timeline of animation is like this:

Order of the events:

  1. Call the apply_card_force function to make the card raise
  2. Freezes the card in the air to wait for the monster’s ray
  3. Instantiate the catchparticle that is going to spawn at the enemy position and will target the card

What I expected was something like this:

What happens in my game :smiling_face_with_tear::

I believe the main issue is that the card is colliding with other objects when the force is applied, causing it to move to random places. Additionally, it’s colliding with other enemies, resulting in all of them being hiding and caught instead of just one. The ray is also hitting the wrong target, likely due to the timing of the animation when the card raises, i can’t figure out how to make this smooth too.

I looks pretty good! I think the particles need their process material edited to stream towards the card, like point the gravity vector towards the card.

Physics objects will go pretty crazy on their own, if you are looking for more granularity it might be best to convert the card to a Area3D or CharacterBody3D. I am surprised your freeze_in_air function works, is the animation player set to physics? Maybe you want to lock some of the rotation axis for the card?

How can I achieve this? My CatchParticle node is structured as follows:

image

I need to change the type of my node? Because my particles is a subnode of a node3D

I see, but it’s not going to bug the physics of the card if i change the node to an Area3D?

Not exactly, the functions are just sync by time, that’s why this acts a bit weird sometimes, i don’t know if it’s necessary to lock the axis of the card.

You can change the GPUParticles3D’s process_material through scripts, I suppose part of your CatchParticle script could say

$GPUParticles3D.process_material.gravity = *direction to card*

It would remove all physics, the “granularity” I’m refering to is “doing it yourself” which in my opinion would be a bad idea, I like how your physics based cards are working, I think you should stick with them. But if the physics side is really more headache than it’s worth you might find “doing it yourself” easier to manage.

1 Like

Do you think it would be possible or advisable to swap the physics based card with a non-physics based card on impact?

Then you have the physics working up to impact - but no physics after the swap.

2 Likes

Ok after a long time struggling to make this works, i think i did it haha, thank you @gertkeno and @nutlike4693 .

So i’ll put my solution here to who wants to make something similar to what i did:

  1. I followed the suggestion and add a Area3D, the Area3D gives you an option to make gravity be replaced and it behaves like a magnet, it would fits well to me except the part that every CatchParticle was attracted by the first area it found.

  2. So to solve that, i add an id for each CatchParticle randomly and also add a script to Area3D to applies the force just in the specific CatchParticle that is created and area that’s interacting with, to make sure that every CatchParticle will be attracted to its correspondent card:

CardPhysicItem:
image

play_catch_particle script:

func play_catch_particle()->void: #This function basically instantiate the ray(gpupartcle3D) that will be appears in the enemy position and will go to the card position in air
	var instance:Object = preload("res://scenes/magic/catch_particle.tscn")
	var instance_energy:CatchParticle = instance.instantiate() #Instantiate the catch_particle in the enemy position
	instance_energy.position = enemy_ref.global_position + Vector3(0, 1, 0) #Add one height unit to ensure the catch particle appears above the ground
	instance_energy.transform.basis = enemy_ref.global_transform.basis
	randomize()
	id_particle = randi() % 100 #id_particle will be a global variable of card_physics_item class
	print("randomize: ", id_particle)
	instance_energy.id = id_particle #set the id to catchparticle
	get_parent().add_child(instance_energy)

Magnet Script:

func _on_magnet_body_entered(body: Node3D) -> void: #Signal to check if a body entered  in the maget area
	if body is CatchParticle: #Test if body is catchparticle
		if body.id == id_particle: #Check id
			# Calculate the direction of body attraction
			var direction = global_transform.origin - body.global_transform.origin
			direction = direction.normalized()
			# Apply the force without the gravity influence
			body.gravity_scale = 0
			body.apply_central_impulse(direction * 1)  
	pass 

It works flawsly well in this scneario:

But it’s still buggy in my game, probably due to the inertia. Maybe adjusting the throw card force would solve this issue:

2 Likes

Congrats on getting it working. Total MVP move coming back to post your solution :slight_smile:

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.