79241549

Date: 2024-12-01 12:37:43
Score: 0.5
Natty:
Report link

Here I calculate velocity and angular velocity from scratch and get behavior of object that I want. But this method double calculations for collision.

from typing import Optional
import arcade
from pyglet.math import Vec2
import numpy as np

# Set how many rows and columns we will have
ROW_COUNT = 10
COLUMN_COUNT = 10

# This sets the WIDTH and HEIGHT of each grid location
WIDTH = 32
HEIGHT = 32

# This sets the margin between each cell
# and on the edges of the screen.
MARGIN = 1

# Do the math to figure out our screen dimensions
SCREEN_WIDTH = ((WIDTH + MARGIN) * COLUMN_COUNT + MARGIN)*2
SCREEN_HEIGHT = ((HEIGHT + MARGIN) * ROW_COUNT + MARGIN)*2
SCREEN_TITLE = "Game example"

DYNAMIC_TYPE = "item"
STATIC_TYPE = "wall"


def cpv(x,y):
    return (x,y)
def cpvrotate(t1,t2):
    x = t1[0]*t2[0] - t1[1]*t2[1]
    y = t1[0]*t2[1] + t1[1]*t2[0]
    return cpv(x,y)

def cpvadd(t1,t2):
    x = t1[0]+t2[0]
    y = t1[1]+t2[1]
    return cpv(x,y)

def cpvsub(t1,t2):
    x = t1[0]-t2[0]
    y = t1[1]-t2[1]
    return cpv(x,y)

def cpvdot(t1,t2):
    x = t1[0]*t2[0]
    y = t1[1]*t2[1]
    return x+y

def cpvcross(t1,t2):
    x = t1[0]*t2[1]
    y = t1[1]*t2[0]
    return x-y

def cpvmult(t1,number):
    return cpv(t1[0]*number,t1[1]*number)

def cpvperp(t1):
    return cpv(-t1[1],t1[0])

def cpvneg(t1):
    return cpv(-t1[0],-t1[1])


def relative_velocity(a, b, r1, r2):
    v1_sum = cpvadd(a.velocity, cpvmult(cpvperp(r1), a.angular_velocity))
    v2_sum = cpvadd(b.velocity, cpvmult(cpvperp(r2), b.angular_velocity))
    return cpvsub(v2_sum, v1_sum)

def normal_relative_velocity(a, b, r1, r2, n):
    return cpvdot(relative_velocity(a, b, r1, r2), n)

def get_con_bounce(a, b, r1, r2, n, arb):
    return normal_relative_velocity(a, b, r1, r2, n)*arb.restitution

def get_vr(a, b, r1, r2, n, surface_vr):
    vr = cpvadd(relative_velocity(a, b, r1, r2), surface_vr)
    return vr

def apply_impulse(body, j, r):
    i_inv = 1.0/body.moment
    m_inv = 1.0/body.mass
    velocity =  cpvmult(j, m_inv)
    angular_velocity = i_inv*cpvcross(r, j)
    return velocity, angular_velocity
    
def k_scalar_body(body, r, n):
    rcn = cpvcross(r, n)
    i_inv = 1.0/body.moment
    m_inv = 1.0/body.mass
    return m_inv + i_inv*rcn*rcn

def k_scalar(a, b, r1, r2, n):
    return k_scalar_body(a, r1, n) + k_scalar_body(b, r2, n)
    
def cpfclamp(f,min_,max_):
    return min(max(f, min_), max_)

def arbApplyImpulse(r1, r2, n, arb, _space, _data, jnAcc=0.0, jtAcc=0.0):
    a = arb.shapes[0].body
    b = arb.shapes[1].body
    nMass = 1.0/k_scalar(a, b, r1, r2, n)
    tMass = 1.0/k_scalar(a, b, r1, r2, cpvperp(n))
    
    surface_vr = arb.surface_velocity
    bounce = get_con_bounce(a, b, r1, r2, n, arb)

    vr = get_vr(a, b, r1, r2, n, surface_vr)
    
    vrn = cpvdot(vr, n)
    vrt = cpvdot(vr, cpvperp(n))
    
    jn = -(bounce + vrn)*nMass
    jnOld = jnAcc
    jnAcc = max(jnOld+jn, 0)
    
    jtMax = arb.friction*jnAcc
    jt = -vrt*tMass
    jtOld = jtAcc
    jtAcc = cpfclamp(jtOld + jt, -jtMax, jtMax)
    
    
    _j = cpvrotate(n, cpv(jnAcc - jnOld, jtAcc - jtOld))
 
    v,w = apply_impulse(a, cpvneg(_j), r1)
    
    
    
    return v, w, bounce

def ArbSetContactPointSet(arb, set_, number, swapped):
    p1 = set_[number].point_a
    p2 = set_[number].point_b
    if swapped:
        r1 = cpvsub(p2, arb.shapes[0].body.position)
        r2 = cpvsub(p1, arb.shapes[1].body.position)
    else:
        r1 = cpvsub(p1, arb.shapes[0].body.position)
        r2 = cpvsub(p2, arb.shapes[1].body.position)
    return r1,r2

def arbApllyImpulses(_arbiter, _space, _data):
    swapped = False
    v_total = (0,0)
    w_total = 0
    bounce_total = []
    for i in range(len(_arbiter.contact_point_set.points)):
        r1,r2 = ArbSetContactPointSet(_arbiter, _arbiter.contact_point_set.points, 
                                      i, swapped)
        #print("r1   r2  ", r1,r2)
        v, w, bounce = arbApplyImpulse(r1, r2, _arbiter.normal, _arbiter,_space, _data)
        bounce_total.append(bounce)
        v_total = cpvadd(v_total,v)
        w_total+=w
    _data["a_w"] += w_total
    
    #CUSTOM VELOCITY
    _data["a_v"] = list(abs(np.average(bounce_total))*(v_total / np.linalg.norm(v_total)))

class MyGame(arcade.Window):
    def __init__(self, width, height, title):
        super().__init__(width, height, title)

        # Set the background color of the window
        self.background_color = arcade.color.BLACK
        
        self.wall_list = arcade.SpriteList()
        self.item_list = arcade.SpriteList()
        
        self.create_map()
        
        self.item_1 = self.create_item(9,1)
        self.item_list.append(self.item_1)
        
        self.physics_engine = Optional[arcade.PymunkPhysicsEngine]
        damping = 0.9
        gravity = (0,-20)
        self.physics_engine = arcade.PymunkPhysicsEngine(damping=damping,
                                                         gravity=gravity)
        self.physics_engine.space.collision_slop = 2
        
        self.physics_engine.add_sprite_list(self.wall_list,
                                    friction=0.2,
                                    collision_type="wall",
                                    elasticity = 1,
                                    body_type=arcade.PymunkPhysicsEngine.STATIC)
        
        self.physics_engine.add_sprite(self.item_1, friction=0.2,collision_type="item", elasticity = 1)
        
        def hit_handler_pre(dynamic_sprite, static_sprite, _arbiter, _space, _data):
            if _arbiter.is_first_contact:
                _data["a_v"] = _arbiter.shapes[0].body.velocity
                _data["a_w"] = _arbiter.shapes[0].body.angular_velocity
                arbApllyImpulses(_arbiter, _space, _data)
            return True
                
        def hit_handler_post(dynamic_sprite, static_sprite, _arbiter, _space, _data):
            if _arbiter.is_first_contact:
                _arbiter.shapes[0].body.velocity = _data["a_v"]
                _arbiter.shapes[0].body.angular_velocity = _data["a_w"]
            
        self.physics_engine.add_collision_handler(DYNAMIC_TYPE, STATIC_TYPE, pre_handler = hit_handler_pre)
        self.physics_engine.add_collision_handler(DYNAMIC_TYPE, STATIC_TYPE, post_handler = hit_handler_post)
        
        # Create the cameras. One for the GUI, one for the sprites.

        # We scroll the 'sprite world' but not the GUI.

        self.camera_sprites = arcade.Camera(SCREEN_WIDTH, SCREEN_HEIGHT)

        self.camera_gui = arcade.Camera(SCREEN_WIDTH, SCREEN_HEIGHT)
        
    
    def scroll_to_item(self, x, y):

        position = Vec2(x - self.width / 2, y - self.height / 2)

        self.camera_sprites.move_to(position, 0.5)
    
    def create_wall_item(self, row, column, width, height):
        x = column * (WIDTH + MARGIN) + (WIDTH / 2 + MARGIN)
        y = row * (HEIGHT + MARGIN) + (HEIGHT / 2 + MARGIN)
        sprite = arcade.SpriteSolidColor(width, height, arcade.color.GRAY)
        sprite.center_x = x
        sprite.center_y = y
        self.wall_list.append(sprite)
    
    def create_map(self):
        # Create a list of solid-color sprites to represent each grid location
        for row in range(-1,ROW_COUNT+1):
            for column in range(-1,COLUMN_COUNT+1):
                if row==-1 or column==-1 or row==ROW_COUNT or column==COLUMN_COUNT:
                    self.create_wall_item(row,column, WIDTH, HEIGHT)
            

    
    def get_sprite_coords_by_cell(self, row, column):
        x = column * (WIDTH + MARGIN) + (WIDTH / 2 + MARGIN)
        y = row * (HEIGHT + MARGIN) + (HEIGHT / 2 + MARGIN)
        return x, y
        
    def create_item(self, row = 1, column = 1):
        x, y = self.get_sprite_coords_by_cell(row, column)
        item_ = arcade.SpriteSolidColor(WIDTH//4, HEIGHT, arcade.color.BLUE)
        item_.center_x = x
        item_.center_y = y
        item_.angle = 0
        return item_

    
        
   

    def on_draw(self):
        """
        Render the screen.
        """
        # We should always start by clearing the window pixels
        self.clear()

        # Select the camera we'll use to draw all our sprites

        self.camera_sprites.use()
    
        self.item_list.draw()
        self.item_1.draw_hit_box(color = arcade.color.GREEN)
        self.wall_list.draw()
        
        
        # Select the (unscrolled) camera for our GUI

        self.camera_gui.use()
    
    def apply_force(self, physics_engine, sprite, force):
        """ Apply force to a Sprite. """
        physics_object = physics_engine.sprites[sprite]
        physics_object.body.apply_force_at_local_point(force, (0, 0))
        
    def apply_force_world_point(self, physics_engine, sprite, force):
        """ Apply force to a Sprite. """
        physics_object = physics_engine.sprites[sprite]
        physics_object.body.apply_force_at_world_point(force, (sprite.center_x, sprite.center_y))
        
        
            
    def on_update(self, delta_time):
        """ Movement and game logic """
        
        self.physics_engine.step()
        x = self.item_1.center_x
        y = self.item_1.center_y
        self.scroll_to_item(x,y)
        



def main():
    MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
    arcade.run()


if __name__ == "__main__":
    main()
Reasons:
  • RegEx Blacklisted phrase (1): I want
  • Long answer (-1):
  • Has code block (-0.5):
  • Self-answer (0.5):
  • Low reputation (0.5):
Posted by: dragondip