def _draw_widget(self, *args) -> None: """Establish the drawing instructions for the widget.""" del args if self.canvas is None: return # TODO: allow user to set rotation/scale origin center = center_of_points_list(self.points) self.canvas.clear() with self.canvas: Color(*self.color) Scale(self.scale, origin=center) Rotate(angle=self.rotation, origin=center) KivyLine(points=self.points, width=self.thickness, cap=self.cap, joint=self.joint, cap_precision=self.cap_precision, joint_precision=self.joint_precision, close=self.close)
def _draw_widget(self, *args) -> None: """Establish the drawing instructions for the widget.""" del args if self.canvas is None: return anchor = (self.x - self.anchor_offset_pos[0], self.y - self.anchor_offset_pos[1]) self.canvas.clear() with self.canvas: Color(*self.color) Rotate(angle=self.rotation, origin=anchor) Scale(self.scale).origin = anchor if self.corner_radius > 0: RoundedRectangle(pos=self.pos, size=self.size, radius=(self.corner_radius, self.corner_radius), segments=self.corner_segments) else: KivyRectangle(pos=self.pos, size=self.size)
def on_angle(self, item, angle): """ Every time the angle of the item changes, this function is called to continue the rotation. Almost like a recursive function as each change in angle runs this which causes another change. :param item: the object being rotated :type: object :param angle: the angle at which the item's rotation will change to :type: int """ item.rotate_animation.stop(self) item.rotate_animation = Animation(angle=1, duration=0.00001) item.rotate_animation.start(self) if int(angle * 360) == 360: item.rotate_animation += Animation(angle=angle + 1, duration=0.00001) item.rotate_animation.start(self) with self.root.canvas.before: PushMatrix() Rotate(angle=angle, axis=(0, 0, 1), origin=self.root.center) with self.root.canvas.after: PopMatrix()
def init_canvas(self): with self.canvas.before: PushMatrix() self.rotation = Rotate(angle=0, origin=self.center) with self.canvas.after: PopMatrix()
def _show_image(config, img, scale=None, translation=None, rotation=None, transform_matrix=None): from kivy.graphics.texture import Texture from kivy.graphics.fbo import Fbo from kivy.graphics import (Mesh, StencilPush, StencilUse, StencilUnUse, StencilPop, Rectangle, Color) from kivy.graphics.context_instructions import (PushMatrix, PopMatrix, Rotate, Translate, Scale, MatrixInstruction, BindTexture) from kivy.graphics.transformation import Matrix img_fmt = img.get_pixel_format() img_w, img_h = img.get_size() size = config['orig_size'] pos = config['pos'] canvas = config['canvas'] if img_fmt not in ('yuv420p', 'rgba', 'rgb24', 'gray', 'bgr24', 'bgra'): ofmt = get_best_pix_fmt( img_fmt, ('yuv420p', 'rgba', 'rgb24', 'gray', 'bgr24', 'bgra')) swscale = SWScale(iw=img_w, ih=img_h, ifmt=img_fmt, ow=0, oh=0, ofmt=ofmt) img = swscale.scale(img) img_fmt = img.get_pixel_format() kivy_ofmt = { 'yuv420p': 'yuv420p', 'rgba': 'rgba', 'rgb24': 'rgb', 'gray': 'luminance', 'bgr24': 'bgr', 'bgra': 'bgra' }[img_fmt] if kivy_ofmt == 'yuv420p': w2 = int(img_w / 2) h2 = int(img_h / 2) tex_y = Texture.create(size=(img_w, img_h), colorfmt='luminance') tex_u = Texture.create(size=(w2, h2), colorfmt='luminance') tex_v = Texture.create(size=(w2, h2), colorfmt='luminance') with canvas: fbo = Fbo(size=(img_w, img_h)) with fbo: BindTexture(texture=tex_u, index=1) BindTexture(texture=tex_v, index=2) Rectangle(size=fbo.size, texture=tex_y) fbo.shader.fs = CeedDataReader._YUV_RGB_FS fbo['tex_y'] = 0 fbo['tex_u'] = 1 fbo['tex_v'] = 2 tex = fbo.texture dy, du, dv, _ = img.to_memoryview() tex_y.blit_buffer(dy, colorfmt='luminance') tex_u.blit_buffer(du, colorfmt='luminance') tex_v.blit_buffer(dv, colorfmt='luminance') else: tex = Texture.create(size=(img_w, img_h), colorfmt=kivy_ofmt) tex.blit_buffer(img.to_memoryview()[0], colorfmt=kivy_ofmt) tex.flip_vertical() with canvas: StencilPush() Rectangle(pos=pos, size=size) StencilUse() PushMatrix() center = pos[0] + size[0] / 2, pos[1] + size[1] / 2 if rotation: Rotate(angle=rotation, axis=(0, 0, 1), origin=center) if scale: Scale(scale, scale, 1, origin=center) if translation: Translate(*translation) if transform_matrix is not None: mat = Matrix() mat.set(array=transform_matrix) m = MatrixInstruction() m.matrix = mat Rectangle(size=(img_w, img_h), texture=tex, pos=pos) PopMatrix() StencilUnUse() Rectangle(pos=pos, size=size) StencilPop()
def update_tiles(self, dt): # print(self.player.x, self.player.y) self.canvas.clear() viewpane_width = (self.width // self.tile_size) // 2 viewpane_height = (self.height // self.tile_size) // 2 fov_dist = 6 center = (viewpane_width + viewpane_height + 1) // 2 # Find Entities within View Pane entities_to_render = {} entities_to_render = self.find_entities_within_fov( viewpane_width, viewpane_height, fov_dist, dt) # Find Particle Entities within View Pane particles_to_render = {} particles_to_render = self.find_particles_within_fov( viewpane_width, viewpane_height, fov_dist, dt) with self.canvas: Color(1, 1, 1, 1.0) # Render Background Tiles Within for y in range(self.viewpane_center_y + viewpane_height + 1, self.viewpane_center_y - viewpane_height - 1, -1): for x in range(self.viewpane_center_x + viewpane_width + 1, self.viewpane_center_x - viewpane_width - 1, -1): Color(1, 1, 1, 1.0) correct_x = x - self.viewpane_center_x + viewpane_width # normalize to l0 thru map width correct_y = y - self.viewpane_center_y + viewpane_height try: 1 // (abs(y + 1) + y + 1) # check for negative numbers 1 // (abs(x + 1) + x + 1) tile_integer = self.map[y][x] tile_name = self.tile_enum[(floor(tile_integer))] tile_clock_ind = floor(self.map_clock[y][x]) tile_tex = self.data[tile_name][tile_clock_ind] except ZeroDivisionError: tile_tex = self.blank_tex # default to black except IndexError: try: self.map_clock[y][x] = 0 tile_tex = self.data[tile_name][0] except: tile_tex = self.blank_tex # default to black Color(1, 1, 1, 1.0) Rectangle( texture=tile_tex, size=tile_tex.size, pos=((correct_x * self.tile_size) + self.parent.x + self.player_x - self.tile_size, (correct_y * self.tile_size) + self.parent.y + self.player_y - self.tile_size)) # Render Trees try: correct_x = x - self.viewpane_center_x + viewpane_width # normalize to 0 thru map width correct_y = y - self.viewpane_center_y + viewpane_height 1 // (abs(y + 1) + y + 1) 1 // (abs(x + 1) + x + 1) tile_integer = self.tree_map[y][x] tile_name = self.tree_enum[(floor(tile_integer))] tile_tex = self.tree_data[tile_name] # Color(1, 1, 1, randint(0, 255) / 255) # Color(1, 1, 1, randint(200, 255) // 255) Rectangle( texture=tile_tex, size=tile_tex.size, pos=((correct_x * self.tile_size) + self.parent.x + self.player_x - self.tile_size, (correct_y * self.tile_size) + self.parent.y + self.player_y - self.tile_size)) except TypeError: # If "None" is encountered from tree_map array with no object placed # continue pass except IndexError: # If x/y coordinates exceed tree_map array # continue pass except ZeroDivisionError: # Check for any x/y coordinates dips below 0, otherwise will select from end of array # continue pass # Render Particle p = particles_to_render.get((x, y)) if p: p.update(dt) correct_x = ( p.x // self.tile_size ) - self.viewpane_center_x + viewpane_width correct_y = ( p.y // self.tile_size ) - self.viewpane_center_y + viewpane_height PushMatrix() r = Rotate() r.angle = p.angle r.axis = (0, 0, 1) # r = Scale() # r.x = p.scale # r.y = p.scale r.origin = (correct_x * self.tile_size + self.parent.x + self.player_x - (self.tile_size // 2)), ( correct_y * self.tile_size + self.parent.y + self.player_y - (self.tile_size // 2)) Color(1, 1, 0, 1.0) Rectangle( texture=p.texture, size=p.texture.size, pos=((correct_x * self.tile_size + self.parent.x + self.player_x - self.tile_size), (correct_y * self.tile_size + self.parent.y + self.player_y - self.tile_size))) PopMatrix() # Render Entities e = entities_to_render.get((x, y)) if e: entity_tex = self.char_data[e.name] correct_x = (e.x + self.tile_size * viewpane_width) - \ (self.viewpane_center_x * self.tile_size) correct_y = (e.y + self.tile_size * viewpane_height ) - (self.viewpane_center_y * self.tile_size) Rectangle(texture=entity_tex, size=entity_tex.size, pos=((correct_x + self.parent.x + self.player_x - self.tile_size), (correct_y + self.parent.y + self.player_y - self.tile_size))) # FOV Rendering for x in range(self.viewpane_center_x + viewpane_width + 1, self.viewpane_center_x - viewpane_width - 1, -1): for y in range(self.viewpane_center_y + viewpane_height + 1, self.viewpane_center_y - viewpane_height - 1, -1): # FOV/Shadow Texture correct_x = x - self.viewpane_center_x + viewpane_width # normalize to 0 thru map width correct_y = y - self.viewpane_center_y + viewpane_height # Outside FOV dist = self.calculate_distance( correct_x - 1, correct_y - 1, center, center) + 0.05 if dist > fov_dist: Color(0, 0, 0, 0.5) Rectangle( texture=self.blank_tex, size=self.blank_tex.size, pos=((correct_x * self.tile_size) + self.parent.x + self.player_x - self.tile_size, (correct_y * self.tile_size) + self.parent.y + self.player_y - self.tile_size))
def drawObj(self, obj=None): ''' Updates a object if object already on widget. Otherwise it will create a object Override this function to create your own apperance of ojects ''' assert isinstance(obj, mrGraphicsObject) if type(obj) == None: return False # get obj attributes objID = obj.getID() objname = obj.getName() location = self.convertPosition( obj.getLocation() ) color = obj.getColor() wObj = None # draw objects with self.canvas: # set color Color( color[0], color[1], color[2], color[3] ) if type(obj) == mrGraphicsBot: # draw bot assert isinstance(obj, mrGraphicsBot) size = self.convertSize( obj.getSize() ) angle = obj.getAngle() lineW, points, points2, corners = self.getBotDrawData(location, size) print "points", points, points2 p0 = corners[0] p3 = corners[3] # draw object wObj = {'widget': Widget( pos=p0 ), 'label': None} lbl = Label( text=str(objID)+":"+objname, pos=p3 ) lbl.texture_update() tsize = lbl.texture.size lbl.size = (tsize[0], tsize[1]) wObj['label'] = lbl # draw dot with wObj['widget'].canvas.before: PushMatrix() Rotate( angle=angle, axis=(0,0,1), origin=(location[0], location[1], 0) ) with wObj['widget'].canvas.after: PopMatrix() with wObj['widget'].canvas: Color( color[0], color[1], color[2], color[3] ) Line( points=points, width=lineW ) Line( points=points2, width=lineW ) if angle != 0: with wObj['label'].canvas.before: PushMatrix() Rotate( angle=angle, axis=(0,0,1), origin=(location[0], location[1], 0) ) with wObj['label'].canvas.after: PopMatrix() if type(obj) == mrGraphicsRectangle: # draw rectangle assert isinstance(obj, mrGraphicsRectangle) size = self.convertSize( obj.getSize() ) pos = ( location[0]-size[0]/2, location[1]-size[1]/2 ) angle = obj.getAngle() # draw new object wObj = Widget( size=size, pos=pos ) # draw rectangle with wObj.canvas: Color( color[0], color[1], color[2], color[3] ) Rectangle( size=size, pos=pos ) if angle != 0: with wObj.canvas.before: PushMatrix() Rotate( angle=angle, axis=(0,0,1), origin=(location[0], location[1], 0) ) with wObj.canvas.after: PopMatrix() if type(obj) == mrGraphicsLine: #draw line assert isinstance(obj, mrGraphicsLine) points = self.convertPoints( obj.getPoints() ) width = self.convertSize( obj.getWidth() ) # draw new object wObj = Line( points=points, width=width ) if type(obj) == mrGraphicsDot: #draw dot assert isinstance(obj, mrGraphicsDot) radius = self.convertSize( obj.getRadius() ) size = (2*radius, 2*radius) pos = ( location[0]-radius, location[1]-radius ) # draw new object wObj = Ellipse( pos=pos, size=size ) if type(obj) == mrGraphicsCircle: #draw cricle assert isinstance(obj, mrGraphicsCircle) radius = self.convertSize( obj.getRadius() ) width = self.convertSize( obj.getWidth() ) # draw new object wObj = Line( circle=(location[0], location[1], radius), width=width ) if type(obj) == mrGraphicsText: #draw text assert isinstance(obj, mrGraphicsText) text = obj.getText() textcolor = obj.getTextColor() fontsize = obj.getFontSize() angle = obj.getAngle() # draw new object padding = 5 wObj = Label( text=text, pos=location, markup=True, color=textcolor ) if fontsize != None: print "set fontsize", fontsize wObj.font_size = fontsize wObj.texture_update() size = wObj.texture.size wObj.size = (size[0]+2*padding, size[1]+2*padding) with wObj.canvas.before: PushMatrix() Rotate( angle=angle, axis=(0,0,1), origin=(location[0], location[1], 0) ) Color( color[0], color[1], color[2], color[3] ) Rectangle( pos=wObj.pos, size=wObj.size) with wObj.canvas.after: PopMatrix() if type(obj) == mrGraphicsImage: # draw image assert isinstance(obj, mrGraphicsImage) src = obj.getSource() keepRatio = obj.keepRatio() size = self.convertSize( obj.getSize() ) angle = obj.getAngle() if isfile(src): wObj = AsyncImage( source=src, size=size, allow_stretch=True, keep_ratio=keepRatio ) wObj.texture_update() pos = (location[0]-size[0]*0.5, location[1]-size[1]*0.5) wObj.pos = pos size = wObj.size if angle != 0: with wObj.canvas.before: PushMatrix() Rotate( angle=angle, axis=(0,0,1), origin=(location[0], location[1], 0) ) with wObj.canvas.after: PopMatrix() # send update request to canvas self.canvas.ask_update() # return new object return wObj
def animate_game_event(self, widget=None, anim_duration=0.2): """ Process a single event from self.animation_queue Read the event and perform the correct actions on widgets (such as update text of log window, create and launch animation, maybe make some sound). The event is removed from self.map.game_events. After the actions required are performed, the method calls itself again, either recursively, or, in case of animations, via Animation's on_complete argument. The recursion is broken when event queue is empty. :return: """ if widget and widget.parent and widget.height == 0: # If the widget was given zero size, this means it should be removed # This entire affair is kinda inefficient and should be rebuilt later widget.parent.remove_widget(widget) if not self.animation_queue == []: event = self.animation_queue.pop(0) if event.event_type == 'moved': final = self.get_screen_pos(event.actor.location, center=True) if final[0] < event.actor.widget.pos[0] and event.actor.widget.direction == 'right'\ or final[0] > event.actor.widget.pos[0] and event.actor.widget.direction == 'left': event.actor.widget.flip() a = Animation(center=final, duration=anim_duration) a.bind(on_start=lambda x, y: self.remember_anim(), on_complete=lambda x, y: self.animate_game_event(widget=y)) a.start(event.actor.widget) elif event.event_type == 'attacked': current = self.get_screen_pos(event.actor.location, center=True) target = self.get_screen_pos(event.location, center=True) if target[0] > current[0] and event.actor.widget.direction == 'left' or\ target[0] < current[0] and event.actor.widget.direction == 'right': event.actor.widget.flip() a = Animation(center_x=current[0]+int((target[0]-current[0])/2), center_y=current[1]+int((target[1]-current[1])/2), duration=anim_duration/2) a += Animation(center_x=current[0], center_y=current[1], duration=anim_duration/2) a.bind(on_start=lambda x, y: self.remember_anim(), on_complete=lambda x, y: self.animate_game_event(widget=y)) a.start(event.actor.widget) self.parent.boombox['attacked'].seek(0) self.parent.boombox['attacked'].play() elif event.event_type == 'was_destroyed': if not event.actor.widget: # If actor is None, that means it was destroyed right after spawning, not getting a # widget. Similar case is covered under 'dropped', see there for example. The check is # different here, because in 'dropped' item is taken from map, where it's None by the time # this method runs. Here, on the other hand, Item object exists (in GameEvent), but has # no widget (and is not placed on map, but that's irrelevant). self.animate_game_event() return a = Animation(size=(0, 0), duration=anim_duration) a.bind(on_start=lambda x, y: self.remember_anim(), on_complete=lambda x, y: self.animate_game_event(widget=y)) a.start(event.actor.widget) elif event.event_type == 'picked_up': # It's assumed that newly added item will be the last in player inventory self.layer_widgets['items'].remove_widget(self.map.actors[0].inventory[-1].widget) self.animate_game_event() elif event.event_type == 'dropped': item = self.map.get_item(location=event.location, layer='items') if not item: # Item could've been destroyed right after being drop, ie it didn't get a widget. Skip. # It's rather likely if someone was killed by landmine, dropped an item and had this item # destroyed in the same explosion self.animate_game_event() return if not item.widget: self.tile_factory.create_widget(item) item.widget.center = self.get_screen_pos(event.location, center=True) self.layer_widgets['items'].add_widget(item.widget) self.animate_game_event() elif event.event_type == 'actor_spawned': if not event.actor.widget: self.tile_factory.create_widget(event.actor) event.actor.widget.center = self.get_screen_pos(event.location, center=True) self.layer_widgets['actors'].add_widget(event.actor.widget) self.animate_game_event() elif event.event_type == 'construction_spawned': if not event.actor.widget: self.tile_factory.create_widget(event.actor) event.actor.widget.center = self.get_screen_pos(event.location, center=True) self.layer_widgets['constructions'].add_widget(event.actor.widget) self.animate_game_event() elif event.event_type == 'exploded': loc = self.get_screen_pos(event.location) loc = (loc[0]+16, loc[1]+16) self.overlay_widget = Image(source='Explosion.png', size=(0, 0), size_hint=(None, None), pos=loc) a = Animation(size=(96, 96), pos=(loc[0]-32, loc[1]-32), duration=0.3) a += Animation(size=(0, 0), pos=loc, duration=0.3) a.bind(on_start=lambda x, y: self.remember_anim(), on_complete=lambda x, y: self.animate_game_event(widget=y)) self.add_widget(self.overlay_widget) self.parent.boombox['exploded'].seek(0) self.parent.boombox['exploded'].play() a.start(self.overlay_widget) elif event.event_type == 'rocket_shot': self.overlay_widget = RelativeLayout(center=self.get_screen_pos(event.actor.location, center=True), size=(64,64), size_hint=(None, None)) i = Image(source='Rocket.png', size=(32, 32), size_hint=(None, None)) self.overlay_widget.add_widget(i) self.overlay_widget.canvas.before.add(Translate(x=16, y=16)) a = degrees(atan2(event.actor.location[1]-event.location[1], event.actor.location[0]-event.location[0])) # if abs(a) >= 90: # self.overlay_widget.center_y += 64 self.overlay_widget.canvas.before.add(Rotate(angle=a+90, axis=(0, 0, 1), origin=i.center)) a = Animation(center=self.get_screen_pos(event.location, center=True), duration=anim_duration) a += Animation(size=(0, 0), duration=0) a.bind(on_start=lambda x, y: self.remember_anim(), on_complete=lambda x, y: self.animate_game_event(widget=y)) self.add_widget(self.overlay_widget) a.start(self.overlay_widget) elif event.event_type == 'shot': self.overlay_widget = Image(source='Shot.png', size=(32, 32), size_hint=(None, None), pos=self.get_screen_pos(event.actor.location)) a = Animation(pos=self.get_screen_pos(event.location), duration=anim_duration) a += Animation(size=(0, 0), duration=0) a.bind(on_start=lambda x, y: self.remember_anim(), on_complete=lambda x, y: self.animate_game_event(widget=y)) self.add_widget(self.overlay_widget) self.parent.boombox['shot'].seek(0) self.parent.boombox['shot'].play() a.start(self.overlay_widget) elif event.event_type in self.non_animated: self.parent.process_nonmap_event(event) self.animate_game_event() else: # Reactivating keyboard after finishing animation self.animating = False # Might as well be time to redraw the Dijkstra widget if DISPLAY_DIJKSTRA_MAP: if self.dijkstra_widget: self.remove_widget(self.dijkstra_widget) # else: # self.map.dijkstras['PC'].rebuild_self() self.dijkstra_widget = DijkstraWidget(parent=self) self.add_widget(self.dijkstra_widget)
def __init__(self, image: str, pos: Tuple[float, float], size: Tuple[float, float], angle: float, **kwargs) -> None: super().__init__(image, pos, size, **kwargs) self.rotate = Rotate(angle=angle, origin=self.get_center())