From b8afa647c5e2263c0c88cb3d9c073a85c7f4d1eb Mon Sep 17 00:00:00 2001 From: Abhas Bhattacharya Date: Thu, 16 May 2013 07:51:33 +0530 Subject: [PATCH] Sync with local mouse keyboard main loop done --- README.md | 2 +- base.py | 396 +++++++++++++++++++++++++++++++++++++++++++++ how i want/pong.py | 25 +++ uniquelist.py | 20 +++ 4 files changed, 442 insertions(+), 1 deletion(-) create mode 100644 base.py create mode 100644 how i want/pong.py create mode 100644 uniquelist.py diff --git a/README.md b/README.md index 2850ffc..96c86ed 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -pyFun +pyFun (Requires pygame) ===== pyFun is a just-started WIP python project aiming to mix the simplicity of Gamemaker with the productiveness of Python and Pygame. diff --git a/base.py b/base.py new file mode 100644 index 0000000..9699061 --- /dev/null +++ b/base.py @@ -0,0 +1,396 @@ +# only for python 2.x +from uniquelist import * # imports uniquelist +import pygame +from pygame.locals import * # imports the constants +#Mouse constants +B_LEFT_CLICK=1 +B_RIGHT_CLICK=3 +B_MIDDLE_CLICK=2 +B_SCROLLUP=4 +B_SCROLLDOWN=5 + +# Direction constants +TOP=1 +LEFT=2 +BOTTOM=3 +RIGHT=4 + +class fun_Game(): + list_class=uniquelist() + list_event=[] # event_list of this step + list_event_old=[] # event_list of the previous step + list_state_all_buttons=[] # A sequence of boolean representing the state of every key + list_state_all_buttons_old=[] # A sequence of boolean representing the state of every key of previous step + def __init__(self,width,height,fps=30): + pygame.init() + self.fps=fps + self.clock=pygame.time.Clock() + self.width=width + self.height=height + + def update_all_class(self): + for temp_cls in fun_Game.list_class: + for temp_count in temp_cls.list_instance: + temp_count.update() + + def _cache_event_list(self): + ''' Cache event_list in every step. + All events in fun_Game.list_event have a "type" property which are the usual constants. ''' + fun_Game.list_event_old=fun_Game.list_event # move current list to old list + fun_Game.list_state_all_buttons_old=fun_Game.list_state_all_buttons + + fun_Game.list_event=pygame.event.get() + fun_Game.list_state_all_buttons=pygame.key.get_pressed() #Returns a sequence of boolean representing the state of every key. Use key constant values for indexing. + #print fun_Game.list_event # debug message + # control access to list_event + # with getter, do something crazy for filtering + # or a filtering function + + def run(self): + self.screen=pygame.display.set_mode([self.width,self.height]) + while True: + self._cache_event_list() # Todo: Add other update-related functions + self.update_all_class() + # VERY IMPORTANT: Do a spare first step so that every variable / list gets initialised well. Start instant creation and execution from second step. + # IMPORTANT: First do all fun_Game related stuff before dealing with game objects. + self.clock.tick(self.fps) + self.screen.fill([50,100,150]) # Todo: Remove this debug statement + pygame.display.flip() # debug statement + #print(event) + #print(fun_Game.list_event_old) + # Todo - Add crazy functions to allow other objects and instances finding like GM + + #class fun_Game ends here + +class meta_fun_Class(type): + def __new__(cls,name,base,clsdict): + temp_class=type.__new__(cls,name,base,clsdict) + fun_Game.list_class.append(temp_class) + cls.priority_order=cls.depth=len(fun_Game.list_class) #by default, priority_order and depth depends on "when class was defined" + + return temp_class + +class fun_Class(object): + __metaclass__=meta_fun_Class + list_instance=uniquelist() + #list_update_functions stores the list of functions to call in the given order. + #It can be used for injecting custom user functions (remember order). + list_update_functions=uniquelist() #action_draw not added as it depends on depth + list_check_functions=uniquelist() # all the check functions added here. what they do: if event_: action_ + + def __init__(self,x,y,img=None): + self.x=0 + self.y=0 + self.img=img #img should be a fun_Image, but lets do that later + fun_Class.list_instance.append(self) + self.action_create() + + def update(self): + '''calls all functions in list_update_functions in the given order''' + temp=None + for temp in fun_Class.list_update_functions: + temp(self) + + def draw(self): + # do some blitting depending on self.img + pass + + def register(list_to_register=None): + ''' Used as decorator: Adds the function to list_to_register ''' + def temp_func(what_to_register): + list_to_register.append(what_to_register) + return what_to_register + return temp_func + + @register(list_update_functions) + def action_begin_step(self): + pass + @register(list_update_functions) + def action_step(self): + tmp=self.event_mouse_click() + if tmp: + print tmp + print(self.mouse_get_focused()) + @register(list_update_functions) + def action_end_step(self): + pass + # creation and destroy event + def action_create(self): + ''' Better to use this function than overloading __init__() ''' + pass + def action_destroy(self): + ''' Executed when the instance is destroyed ''' + pass + def destroy(self): + action_destroy(self) + + # checks for all standard events -- functions named like check_ + # what they do: if event_: action_ + # so lets define event_,action_,check_ for each + + # keyboard handling (later move to keyboard module) + + # occurs if the key was held down continously since the last step + def event_key_pressed(self,key_value):#check if it works + if get_event_list(list_to_get=fun_Game.list_state_all_buttons)[key_value] \ + and get_event_list(list_to_get=fun_Game.list_state_all_buttons_old)[key_value]\ + and (not get_event_list(event_types=KEYUP,further_check_variable_name="key",further_check_value=key_value))\ + and (not get_event_list(event_types=KEYUP,further_check_variable_name="key",further_check_value=key_value,list_to_get=fun_Game.list_event_old)): + return True + + def action_key_pressed(self,key_value): + pass + def check_key_pressed(self): + pass # Todo: think about how to do it + + # occurs if the key is held at the moment (live) + def event_key_pressed_live(self,key_value):#check if it works + ''' Returns True if the key (represented by key_code) is currently pressed. + Problem is that this function is live. Returns True if the key is pressed at that moment. + So, for example, two calls in two consecutive steps may return True although the key has been left in the meantime. ''' + temp_all_buttons=get_event_list(list_to_get=fun_Game.list_state_all_buttons) #Returns a sequence of boolean representing the state of every key. Use key constant values for indexing. + if temp_all_buttons[key_value]: # indexing by key_value + return True + def action_key_pressed_live(self,key_value): + pass + def check_key_pressed_live(self): + temp_all_buttons=get_event_list(list_to_get=fun_Game.list_state_all_buttons) #Returns a sequence of boolean representing the state of every key. Use key constant values for indexing. + for temp in range(len(temp_all_buttons)): # temp represents the key code in numbers as it is the index of list_state_all_buttons indirectly + if temp_all_buttons[temp]: + self.action_key_pressed_live(temp) #temp is the keycode + + # occurs if a key press occurs (i.e. pushed down) + # key repetations may occur as the list is cached per step and there might be multiple (same key)press per step. + def event_key_down(self,key=None): + if key == None: + return get_event_list(KEYDOWN) + else: + return get_event_list(KEYDOWN,"key",key) + def action_key_down(self,key): + pass + def check_key_down(self): + for temp in get_event_list(KEYDOWN): + action_key_down(self,temp.key) + + + #probably wierd thing to do - define all <>_key_down as equivalent to <>_key_press + event_key_press = event_key_down + action_key_press = action_key_down + check_key_press = check_key_down + + # occurs if key is released + def event_key_up(self,key=None): + if key==None: + return get_event_list(KEYUP) + else: + return get_event_list(KEYUP,"key",key) + def action_key_up(self,key): + pass + def check_key_up(self): + for temp in get_event_list(KEYUP): + action_key_up(self,temp.key) + # Todo: Do other key and keyboard related functions (look at GM for related functions) + # Mouse handling (later move it to mouse module) + #Mouse constants (defined at top - move them to a constants module, which will be imported in the global namespace) + #mouse click + def event_mouse_click(self,button=None): + if button==None: + return get_event_list(event_types=MOUSEBUTTONDOWN) + else: + return get_event_list(event_types=MOUSEBUTTONDOWN,further_check_variable_name="button",further_check_value=button) + def action_mouse_click(self,button): + pass + def check_mouse_click(self): + list_temp=get_event_list(event_types=MOUSEBUTTONDOWN) + for temp in list_temp: + action_mouse_click(temp.button) + #mouse get pressed + def event_mouse_get_pressed(self,button=None): + ''' event_mouse_get_pressed() -> tuple looking like (1,0,0) i.e. ( left_click_state, middle_click_state, right_click_state ). + Use an index which is (corresponding_constant - 1). For eg event_mouse_get_pressed()[0] returns left click state. + event_mouse_get_pressed(button) -> Returns 0 or 1 depending on pressed or not. ''' + if button==None: + return pygame.mouse.get_pressed() + else: + return pygame.mouse.get_pressed()[button-1] # because the tuple returned has o-based indexing + # end of mouse handling + # other mouse-based functions + def mouse_get_focused(self): + return pygame.mouse.get_focused() + def mouse_set_visible(self,bool_value): + return pygame.mouse.set_visible(bool_value) + def mouse_get_rel(self): + ''' Returns relative movement of the mouse since the last call to this function. + mouse_get_rel() -> (x,y) ''' + return pygame.mouse.get_rel() + def mouse_set_cursor_image(self,image=None): + pass # todo: complete after the image part is done + # implement mouse_pos as variable + @property + def mouse_pos(self): + ''' mouse_pos -> (x,y) + mouse_pos = [x,y] -> Sets cursor position ''' + return pygame.mouse.get_pos() + @mouse_pos.setter + def mouse_pos(self,val): + ''' val should be a list [x,y] ''' + if not isinstance(val,list): + raise TypeError("val should be a list [x,y]") + pygame.mouse.set_pos(val) + + # implement mouse_x as variable + @property + def mouse_x(self): + ''' mouse_x -> x + mouse_x = some_x -> Sets cursor x position ''' + return pygame.mouse.get_pos()[0] # get_pos -> (x,y) + @mouse_x.setter + def mouse_x(self,x): + pygame.mouse.set_pos([x,mouse_y]) + + # implement mouse_y as variable + @property + def mouse_y(self): + ''' mouse_y -> y + mouse_y = some_y -> Sets cursor y position ''' + return pygame.mouse.get_pos()[1] # get_pos -> (x,y) + @mouse_y.setter + def mouse_y(self,y): + pygame.mouse.set_pos([mouse_x,y]) + # end of mouse-related function + #other various events + + def event_intersect_room_boundary(self): # todo: test after image part is done + ''' Checks if the object (its image bounding box) is intersecting the room boundary, but not totally outside the room. + Returns direction constants like TOP, LEFT, BOTTOM, RIGHT. ''' + if (self.x+self.image.bbox.width/2)>fun_Game.room_width and not (self.x-self.image.bbox.width/2)>fun_Game.room_width: + return RIGHT # all constants defined at global level + elif (self.y+self.image.bbox.height/2)>fun_Game.room_height and not (self.y-self.image.bbox.height/2)>fun_Game.room_height: + return BOTTOM + elif (self.x-self.image.bbox.width/2)<0 and not (self.x+self.image.bbox.width/2)<0: + return LEFT + elif (self.y-self.image.bbox.height/2)<0 and not (self.y+self.image.bbox.height/2)<0: + return TOP + def action_intersect_room_boundary(self): + pass + def check_intersect_room_boundary(self): + if event_intersect_room_boundary(self): + action_intersect_room_boundary(self) + + def event_outside_room_boundary(self): + ''' Checks if the object (its image bounding box) is totally outside room. + Returns direction constants like TOP, LEFT, BOTTOM, RIGHT. ''' + if (self.x-self.image.bbox.width/2)>fun_Game.room_width: + return RIGHT # all constants defined at global level + elif (self.y-self.image.bbox.height/2)>fun_Game.room_height: + return BOTTOM + elif (self.x+self.image.bbox.width/2)<0: + return LEFT + elif (self.y+self.image.bbox.height/2)<0: + return TOP + + # end of fun_Class + +# functions outside all classes +def get_event_list(event_types=None,further_check_variable_name=None,further_check_value=None,list_to_get=None):#list_to_get is attached to a constant list here + ''' Get the required events from the event_list. + get_event_list([event_types],[further_check_variable_name],further_check_value=None,list_to_get=fun_Game.list_event): + event_types (optional): event_name|list[event_name] + get_event_list() -> returns whole event_list(to be precise, list_to_get) + get_event_list(events) -> returns event_list(to be precise, list_to_get) filtered to contain only "events" + get_event_list([events],further_check_variable_name="var_name",further_check_value=value) -> + returns sub-list containing those elements of get_event_list([events]) for which var_name=value + Potential candidates for list_to_get -> fun_Game.list_event, fun_Game.list_event_old, fun_Game.list_state_all_buttons, fun_Game.list_state_all_buttons_old''' + if list_to_get==None: + list_to_get=fun_Game.list_event_old + if further_check_variable_name: + further_check_enabled=True + else: + further_check_enabled=False + if event_types: + if not hasattr(event_types,"__iter__"): + event_types=[event_types] # if not iterable, make a list out of it + def func_filter(): + list_return=[] + if not event_types: + return list_to_get + for temp_1 in list_to_get: # all events in fun_Game.list_event have a "type" property + for temp_2 in event_types: + if temp_1.type==temp_2: + list_return.append(temp_1) + return list_return + def further_check_filter(temp_list): # returns part of temp_list which passes further_check + list_return=[] + if further_check_enabled: + for temp in temp_list: + if getattr(temp,further_check_variable_name)==further_check_value:# may raise error if further_check_variable_name is not present for all members of the list + list_return.append(temp) + else: + list_return=temp_list + return list_return + return further_check_filter(func_filter()) # returns elements common in fun_Game.list_events and events which are accepted after further_check +# end of get_event_list() + +#following constants are required for the next function +UNION=1 +INTERSECTION=2 +DIFFERENCE=3 +ADD=4 +MAKE_IT_VARIABLE_LIST=5 # for each member (usually objects) in the list, it is replaced by one of its variables +def operation_on_lists(operation,list1,list2=None,variable_name=None): + ''' operation_on_lists(operation, list1, list2) -> list + Does operation involving list1 and list2. + variable_name is required only for MAKE_IT_VARIABLE_LIST operation. It is a string representing the variable name + Valid operations are UNION, INTERSECTION, DIFFERENCE, ADD, MAKE_IT_VARIABLE_list ''' + def intersection(): + if list2==None: + raise TypeError("second argument i.e. list2 should be a list") + list_return=[] + for temp1 in list1: + for temp2 in list2: + if temp1==temp2: + list_return.append(temp1) + return list_return + def union(): + if list2==None: + raise TypeError("second argument i.e. list2 should be a list") + list_return = uniquelist() + list_return.extend(list1) + list_return.extend(list2) + return list_return + def add(): + if list2==None: + raise TypeError("second argument i.e. list2 should be a list") + list_return=[] + list_return.append(list1) + list_return.append(list2) + return list_return + def difference(): + if list2==None: + raise TypeError("second argument i.e. list2 should be a list") + list_return=[] + list_intersection=intersection() + for temp1 in list1: + if temp1 not in list_intersection: + list_return.append(temp1) + return list_return + def make_it_variable_list(): # for each member (usually objects) in the list, it is replaced by one of its variables + if variable_name==None: + raise TypeError("variable_name should be a string") + if not list2==None: + raise TypeError("list2 should be None") + return_list=[getattr(temp,variable_name) for temp in list1] + + if operation==UNION: + return union() + elif operation==INTERSECTION: + return intersection() + elif operation==DIFFERENCE: + return difference() + elif operation==ADD: + return add() + elif operation==MAKE_IT_VARIABLE_LIST: + return make_it_variable_list() + else: + raise ValueError("Invalid operation name. Valid operations are UNION, INTERSECTION, DIFFERENCE.") +# end of operation_on_lists() \ No newline at end of file diff --git a/how i want/pong.py b/how i want/pong.py new file mode 100644 index 0000000..3885442 --- /dev/null +++ b/how i want/pong.py @@ -0,0 +1,25 @@ +import pyFun +class pong(pyFun.fun_Game): + class bat(pyFun.fun_Class): + def __init__(self,up_key,down_key,*args,**kwargs): + super(bat,self).__init__(*args,**kwargs) + self.up_key=up_key + self.down_key=down_key + self.img=pyFun.shapes.rect(w=24,h=96) + + def action_create(self): + self.move_step=3 + + def action_key_press(self,key): + if key==self.up_key: + self.y-=self.move_step + if key==self.down_key: + self.y+=self.move_step + + def action_touch_room_boundary(self): + self.y=self.y_prev + +game=pong(width=760,height=640,fps=60) +game.bat(up_key=k_up,down_key=k_down,x=80,y=game.h/2) +game.bat(up_key=k_w,down_key=k_s,x=game.w-80,y=game.h/2) +game.run() \ No newline at end of file diff --git a/uniquelist.py b/uniquelist.py new file mode 100644 index 0000000..0561bab --- /dev/null +++ b/uniquelist.py @@ -0,0 +1,20 @@ +class uniquelist(list): + ''' A list whose every value is unique. + uniquelist.append(val) or __setitem__(pos,val) i.e. uniquelist[pos]=val is ignored if val is already in uniquelist. + Returns True if the value is added, else returns False. ''' + def __setitem__(self,pos,val): + if val not in self: + super(uniquelist,self).__setitem__(pos,val) + return True + else: + return False + def append(self,val): + if val not in self: + super(uniquelist,self).append(val) + return True + else: + return False + def extend(self,iterable): + for temp in iterable: # todo: use yield so that it is not copied into memory + self.append(temp) + # Todo: Allow for type checking by allowing a type_of_members to be set \ No newline at end of file