A continuation of the previous question
How exactly do we detect collision from body_set_force_integration_callback?
For context, we have a body RID:
var _body:RID
And we set a callback with body_set_force_integration_callback:
Physics2DServer.body_set_force_integration_callback(_body, self, "_body_moved", 0)
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
pass
Before going further, I want to point out that the the final parameter of body_set_force_integration_callback is what we get in _user_data. But, if I set it to null Godot will not pass two arguments to the call, in which case I should define _body_moved with only the state parameter.
Godot will be calling our _body_moved every physics frame if the state of the body is active (i.e. not sleeping).
Note: We need to call body_set_max_contacts_reported and set how many contacts we want reported, for example:
Physics2DServer.body_set_max_contacts_reported(_body, 32)
Now, in the Physics2DDirectBodyState we get contacts, and we can ask what a few things about each contact, including the body:
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
for index in state.get_contact_count():
var body:RID = state.get_contact_collider(index)
var instance:Object = state.get_contact_collider_object(index)
If it is the body of a PhysicsBody2D, then instance will have it.
If we want to implement body_entered and body_exited, we need to keep track of the bodies. I'll keep a dictionary of the instances (i.e. PhysicsBody2D) and I'll use it to report get_colliding_bodies too.
Then we need to keep track of shapes for body_shape_entered and body_shape_exited, not only bodies. We can find them out like this:
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
for index in state.get_contact_count():
var body:RID = state.get_contact_collider(index)
var instance:Object = state.get_contact_collider_object(index)
var body_shape_index:int = state.get_contact_collider_shape(index)
var local_shape_index:int = state.get_contact_local_shape(index)
Notice they are not RID. They are the position of the shape in the body (so 0 is the first shape of the body, 1 is the second one, and so on). This means that we cannot keep track of the shapes separately from the bodies, because they shape indexes do not make sense without knowing to what body they belong. That means we cannot simple use two arrays of bodies like we did before.
Also, if we only have one shape - which was the case of the prior answer - we could ignore local_shape_index because it is always 0. In which case I only need a Dictionary indexed by body:RID of body_shape_index:int.
If I don't take that assumption, I struggle to decide the data structure.
I could use a Dictionary indexed by body:RID of Dictionary indexed by body_shape_index:int of local_shape_index:int, in which case I want helper methods to deal with it, which pushes me to make a class for it.
I could use a Dictionary indexed by body:RID of tuples of body_shape_index:int and local_shape_index:int. Except there is no tuple type, so I would cheat and use Vector2.
You know what? I'll cheat and use the Vector2.
signal body_entered(body)
signal body_exited(body)
signal body_shape_entered(body_rid, body, body_shape_index, local_shape_index)
signal body_shape_exited(body_rid, body, body_shape_index, local_shape_index)
var colliding_instances:Dictionary = {}
var colliding_shapes:Dictionary = {}
func _body_moved(state:Physics2DDirectBodyState, _user_data) -> void:
var old_colliding_shapes:Dictionary = colliding_shapes
var new_colliding_shapes:Dictionary = {}
colliding_shapes = {}
var instances:Dictionary = {}
for index in state.get_contact_count():
# get contact information
var body:RID = state.get_contact_collider(index)
var instance:Object = state.get_contact_collider_object(index)
var body_shape_index:int = state.get_contact_collider_shape(index)
var local_shape_index:int = state.get_contact_local_shape(index)
var vector := Vector2(body_shape_index, local_shape_index)
# add to instances
instances[body] = instance
# add to colliding_shapes
if not colliding_shapes.had(body):
colliding_shapes[body] = [vector]
else:
colliding_shapes[body].append(vector)
# remove from old_colliding_shapes or add to new_colliding_shapes
# there is room for optimization here
if (
old_colliding_shapes.has(body)
and old_colliding_shapes[body].has(vector)
):
old_colliding_shapes[body].erase(vector)
if old_colliding_shapes[body].size() == 0:
old_colliding_shapes.erase(body)
else:
if not new_colliding_shapes.had(body):
new_colliding_shapes[body] = [vector]
else:
new_colliding_shapes[body].append(vector)
for body in old_colliding_shapes.keys():
# get instance from old dictionary
var instance:Object = colliding_instances[body]
# emit
if not instances.has(body):
emit_signal("body_exited", body)
for vector in old_colliding_shapes[body]:
emit_signal(
"body_shape_exited",
body,
instance,
vector.x,
vector.y
)
for body in new_colliding_shapes.keys():
# get instance from new dictionary
var instance:Object = instances[body]
# emit
for vector in old_colliding_shapes[body]:
emit_signal(
"body_shape_entered",
body,
colliders[body],
vector.x,
vector.y
)
if not colliding_instances.has(body):
emit_signal("body_entered", body)
# swap instance dictionaries
colliding_instances = instances
func get_colliding_bodies() -> Array:
return colliding_instances.values()
The variable old_colliding_shapes begins with the shapes already known to be colliding, and in in the iteration we are removing each one we see. So at the end, it has the shapes that were colliding but no longer are.
The variable new_colliding_bodies begins empty, and in the iteration we are adding each shape we didn't remove from old_colliding_shapes, so at the end it has the shapes that are colliding that we didn't know about before.
Notice that old_colliding_shapes and new_colliding_bodies are mutually exclusive. If a body is in one it is not in the other because we only add the body to new_colliding_bodies when it is not in old_colliding_shapes. But since they have shapes and not bodies a body can appear in both. This why I need an extra check to emit "body_exited" and "body_entered".
Related
I am trying to create custom parameters for a Haiku hk.Module.
I know that hk.get_parameter() does create new parameters/returns existing parameters, however, I also want to modify the values of those parameters before they are returned when calling the .init() function. Modifying the parameters after the return from the .init() is straightforward as mentioned in the following StackOverflow thread: How does one get a parameter from a params (pytree) in haiku? (jax framework) I have tried using hk.custom_getter(), however, it only modifies the return value to the user (when calling hk.get_parameter()); the original parameters remain unmodified.
Is there a way to achieve this?
Here is a code snippet of what I am trying to achieve:
class HaikuModule(hk.Module):
def __call__(self):
w = hk.get_parameter("w", shape=[5,5], init=jnp.zeros) # create parameter w intialized to 0s
# modify the value of w
???
def forward_fn():
model = HaikuModule()
return model()
fn = hk.transform(forward_fn)
rng= jax.random.PRNGKey(10)
params = fn.init(rng)
# params should return modified params, not 0s(initialized value)
I am trying to modify the player's movement speed depending on if they are in a bush or not. This is the gist of what I am trying to accomplish:
const Grass = preload("res://World/Grass/Grass.tscn")
onready var grass = Grass.instance()
func move():
grass = Grass.instance()
if grass.player_in_grass():
velocity = move_and_slide(velocity / BUSH_FRICTION_MULTIPLIER)
else:
velocity = move_and_slide(velocity)
The issue that I am having is that I cannot figure out what the code to check should be. I have tried to create a player detection zone for the grass, switching its value when inside of it:
var player = null
func player_is_visible():
return player != null
func _on_PlayerDetectionZone_body_entered(body):
player = body
func _on_PlayerDetectionZone_body_exited(body):
player = null
And my Grass.gd looks like this:
onready var playerDetectionZone = $PlayerDetectionZone
func player_in_grass():
if playerDetectionZone.player != null:
return true
else:
return false
After all of this, I am hit with the error:
Invalid get index 'player' (on base: 'Nil').
The error forwards me to 'if playerDetectionZone.player != null:'.
What am I doing wrong? Should I be checking for this/doing this a different, easier way that you know of? All feedback appreciated. Thank you.
To sum it up:
The error is
Invalid get index 'player' (on base: 'Nil').
And the line of code where this error occurs is:
if playerDetectionZone.player != null:
(which is unfortunately not part of the posted code).
This error means that the variable playerDetectionZone has the value null. Assuming the rest of the code is as posted the problem should be located here:
onready var playerDetectionZone = $PlayerDetectionZone
Something is wrong with the node path ($PlayerDetectionZone). Spelling, capitalisation, maybe wrong position in the tree. Could be a number of things.
Edit: Based on your comment it is probably the wrong position in the tree. The line above only works if the PlayerDecetionZone node is a child of the grass node (the one with grass.gd attached).
Currently I loop through a list of image names and make them into a tkinter PhotoImage. These are saved as variables under their respective names from the list using vars()[var].
In this case vars()[var] represents the value of the item in a list making it into a variable name for the images.
example shown below:
list = ["a","b","c","d"] #and so on...
image_id = []
for x in range((len)list):
var = list[x]
vars()[var] = tk.PhotoImage(file = var + ".gif")
image_id.append(vars()[var]) #this adds the identity of all photoimages to a list for later use
According to my tests I can use the images via the variable names or identities as long as it is within the same function. This is due to the variables being local.
The problem is that even though the image identities are in a list they do not work when used outside of the original function. I believe this happens because the images are tied to their variable names and since those are all local tkinter garbage collects them resulting in my error which is "pyimagenumber does not exist." This is because the identity for a tkinter image is just "pyimage" and its corresponding number. My line of thinking is that I want to make all variable names that store images to be global without needing an extra line per image since that defeats the purpose of using a loop and list. Is there any way i can set the "vars()[var]" to be global while also giving it a value?
For any solutions I would like to avoid fundamental changes or using pillow.
Any help is appreciated and if you have further questions about context please ask!
Simply move your empty list outside of your function. Also instead of for x in range(len(list)), use for x in something instead. It could be something like this:
import tkinter as tk
root = tk.Tk()
image_id = []
def image_creation():
imlist = ["a","b","c","d"] #don't name it as list - it shadows the built in list
for var in imlist:
image_id.append(tk.PhotoImage(file = var + ".gif"))
... #the rest of your code
root.mainloop()
There's no need to use vars(). Just create a dictionary in the global namespace and add your images to it. You can even use the original item from the list as the key.
Example:
global images
...
list = ["a","b","c","d"]
images = {}
for x in list:
images[x] = tk.PhotoImage(file = var + ".gif")
Then, the image for "a" can be accessed at any time as images['a'].
I want to compute some custom variable based on the other block values in the StructBlock and add this custom variable to the template context. Essentially I should be able to use this computed variable in the StructBlock template like so {{ value.custom }}.
Here's my StructBlock:
class BaseBlock(blocks.StructBlock):
bool_fld = blocks.BooleanBlock(required=False, default=False)
def get_context(self, *a, **kw):
ctx = super().get_context(*a, **kw)
ctx['custom'] = 1 if self.bool_fld else 0
return ctx
And the error:
'BaseBlock' object has no attribute 'bool_fld'
Any ideas?
The get_context method on block objects receives the block value as its first argument - in the case of StructBlock, this is a dict-like object whose fields can be accessed as value['some_field'].
class BaseBlock(blocks.StructBlock):
bool_fld = blocks.BooleanBlock(required=False, default=False)
def get_context(self, value, parent_context=None):
ctx = super().get_context(value, parent_context=parent_context)
ctx['custom'] = 1 if value['bool_fld'] else 0
return ctx
See also the get_context example at http://docs.wagtail.io/en/v2.0/topics/streamfield.html#template-rendering.
self.bool_fld won't work here, because Block instances do not hold values themselves - they just act as converters between different data representations. (If you've worked with Django form field objects like forms.CharField, blocks are very similar; both block objects and form field objects know how to render values passed to them as form fields, but they don't hold on to those values.)
Say I have an entry form for a GUI window that asks for a new setting and a value for that setting, which it then passes to another object that manages settings and expects **kwargs as input:
class SettingsForm(someFormClass):
def onSubmit(self):
new_setting = self.content['new setting'].get()
new_value = self.content['value'].get()
settings_instance.add_setting(new_setting=new_value)
This sets new_value to the literal string 'new_setting'. I've tried various ways to get around this, such as using a dictionary:
class SettingsForm(someFormClass):
def onSubmit(self):
new_setting = self.content['new setting'].get()
new_value = self.content['value'].get()
mydict = {}
mydict[new_setting] = new_value
settings_instance.add_setting(**mydict)
This works, but doesn't make much sense for a single pair of values... is there an obvious way that I'm missing?