-
Notifications
You must be signed in to change notification settings - Fork 93
Getting Started 5: Controlling the Viewing Area
The GameView is a GameSystem that does not hold its own entities but operates on other systems. GameView has properties for controlling the viewing area, and every GameSystem have a gameview StringProperty. When the system_id of a GameView is set for a GameSystem.gameview, that GameSystem will be added to the GameView widget instead of directly to the GameWorld. This allows you to control the viewing area of some parts of your game but not others, and also handle transforming the touch input.
In this tutorial, we will begin with the code from Getting Started 4.
The content of this tutorial covers the directories:
examples/6_controlling_the_viewing_area
Prerequisites: You will need to have compiled kivent_core, kivent_cymunk, and cymunk, in addition to having kivy and all its requirements installed.
Into our GameWorld definition in kv we will add the GameView, let's call it 'camera1':
<TestGame>:
gameworld: gameworld
app: app
GameWorld:
id: gameworld
gamescreenmanager: gamescreenmanager
size_of_gameworld: 100*1024
zones: {'general': 20000, 'touch': 100}
PositionSystem2D:
system_id: 'position'
gameworld: gameworld
zones: ['general', 'touch']
RotateSystem2D:
system_id: 'rotate'
gameworld: gameworld
zones: ['general']
RotateRenderer:
gameworld: gameworld
zones: ['general']
shader_source: 'assets/glsl/positionrotateshader.glsl'
gameview: 'camera1'
CymunkPhysics:
gameworld: root.gameworld
zones: ['general']
CymunkTouchSystem:
gameworld: root.gameworld
zones: ['touch']
zone_to_use: 'touch'
physics_system: 'cymunk_physics'
touch_radius: 30
gameview: 'camera1'
GameView:
system_id: 'camera1'
gameworld: gameworld
size: root.size
window_size: root.size
pos: root.pos
do_scroll: True
We need to tell the GameView how big it is and where we want it, here we want it to take up the whole screen so we set size to the root size and pos to the root pos.
do_scroll is a BooleanProperty that control whether or not the user can touch/mouse drag to pan the viewing area. We will set it to True so we can look around as the asteroids drift off screen.
Notice that we have set the gameview StringProperty for both RotateRenderer and CymunkTouchSystem. This is because RotateRenderer will draw tot he gameview's viewing area and we must transform the touch on the screen into camera space in order to accurately detect which object we are touching.
First let us add the new GameView to our init_game function:
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
self.gameworld.init_gameworld(
['cymunk_physics', 'rotate_renderer', 'rotate', 'position',
'cymunk_touch', 'camera1'],
callback=self.init_game)
Secondly, let's adjust the draw_some_stuff function to now draw within the bounds of the GameView, instead of just 0 and the Window.size.
def draw_some_stuff(self):
delete_time = 2.5
gameview = self.gameworld.system_manager['camera1']
x, y = int(-gameview.camera_pos[0]), int(-gameview.camera_pos[1])
w, h = int(gameview.size[0] + x), int(gameview.size[1] + y)
create_asteroid = self.create_asteroid
for i in range(100):
pos = (randint(x, w), randint(y, h))
ent_id = create_asteroid(pos)
self.app.count += 100
We must take the negative of the camera_pos as it moves with the inverse of the viewing field (so that entity pos + camera_pos = pos of entity in screen space). Finally, we add the position of the camera to the size of the GameView to get the right and top of the camera's area.
The GameView has other properties to allow it to follow a specific entity, scale in and out, and more related functionality: see the documentation for details.
main.py
from kivy.app import App
from kivy.uix.widget import Widget
from kivy.clock import Clock
from kivy.core.window import Window
from random import randint, choice
from math import radians, pi, sin, cos
import kivent_core
import kivent_cymunk
from kivent_core.gameworld import GameWorld
from kivent_core.managers.resource_managers import texture_manager
from kivent_core.systems.renderers import RotateRenderer
from kivent_core.systems.position_systems import PositionSystem2D
from kivent_core.systems.rotate_systems import RotateSystem2D
from kivent_cymunk.interaction import CymunkTouchSystem
from kivy.properties import StringProperty, NumericProperty
from functools import partial
texture_manager.load_atlas('assets/background_objects.atlas')
class TestGame(Widget):
def __init__(self, **kwargs):
super(TestGame, self).__init__(**kwargs)
self.gameworld.init_gameworld(
['cymunk_physics', 'rotate_renderer', 'rotate', 'position',
'cymunk_touch', 'camera1'],
callback=self.init_game)
def init_game(self):
self.setup_states()
self.set_state()
def destroy_created_entity(self, ent_id, dt):
self.gameworld.remove_entity(ent_id)
self.app.count -= 1
def draw_some_stuff(self):
delete_time = 2.5
gameview = self.gameworld.system_manager['camera1']
x, y = int(-gameview.camera_pos[0]), int(-gameview.camera_pos[1])
w, h = int(gameview.size[0] + x), int(gameview.size[1] + y)
create_asteroid = self.create_asteroid
for i in range(100):
pos = (randint(x, w), randint(y, h))
ent_id = create_asteroid(pos)
self.app.count += 100
def create_asteroid(self, pos):
x_vel = randint(-500, 500)
y_vel = randint(-500, 500)
angle = radians(randint(-360, 360))
angular_velocity = radians(randint(-150, -150))
shape_dict = {'inner_radius': 0, 'outer_radius': 22,
'mass': 50, 'offset': (0, 0)}
col_shape = {'shape_type': 'circle', 'elasticity': .5,
'collision_type': 1, 'shape_info': shape_dict, 'friction': 1.0}
col_shapes = [col_shape]
physics_component = {'main_shape': 'circle',
'velocity': (x_vel, y_vel),
'position': pos, 'angle': angle,
'angular_velocity': angular_velocity,
'vel_limit': 250,
'ang_vel_limit': radians(200),
'mass': 50, 'col_shapes': col_shapes}
create_component_dict = {'cymunk_physics': physics_component,
'rotate_renderer': {'texture': 'asteroid1',
'size': (45, 45),
'render': True},
'position': pos, 'rotate': 0, }
component_order = ['position', 'rotate', 'rotate_renderer',
'cymunk_physics',]
return self.gameworld.init_entity(
create_component_dict, component_order)
def update(self, dt):
self.gameworld.update(dt)
def setup_states(self):
self.gameworld.add_state(state_name='main',
systems_added=['rotate_renderer'],
systems_removed=[], systems_paused=[],
systems_unpaused=['rotate_renderer'],
screenmanager_screen='main')
def set_state(self):
self.gameworld.state = 'main'
class DebugPanel(Widget):
fps = StringProperty(None)
def __init__(self, **kwargs):
super(DebugPanel, self).__init__(**kwargs)
Clock.schedule_once(self.update_fps)
def update_fps(self,dt):
self.fps = str(int(Clock.get_fps()))
Clock.schedule_once(self.update_fps, .05)
class YourAppNameApp(App):
count = NumericProperty(0)
if __name__ == '__main__':
YourAppNameApp().run()
yourappname.kv
#:kivy 1.9.0
TestGame:
<TestGame>:
gameworld: gameworld
app: app
GameWorld:
id: gameworld
gamescreenmanager: gamescreenmanager
size_of_gameworld: 100*1024
zones: {'general': 20000, 'touch': 100}
PositionSystem2D:
system_id: 'position'
gameworld: gameworld
zones: ['general', 'touch']
RotateSystem2D:
system_id: 'rotate'
gameworld: gameworld
zones: ['general']
RotateRenderer:
gameworld: gameworld
zones: ['general']
shader_source: 'assets/glsl/positionrotateshader.glsl'
gameview: 'camera1'
CymunkPhysics:
gameworld: root.gameworld
zones: ['general']
CymunkTouchSystem:
gameworld: root.gameworld
zones: ['touch']
zone_to_use: 'touch'
physics_system: 'cymunk_physics'
touch_radius: 30
gameview: 'camera1'
GameView:
system_id: 'camera1'
gameworld: gameworld
size: root.size
window_size: root.size
pos: root.pos
do_scroll: True
GameScreenManager:
id: gamescreenmanager
size: root.size
pos: root.pos
gameworld: gameworld
<GameScreenManager>:
MainScreen:
id: main_screen
<MainScreen@GameScreen>:
name: 'main'
FloatLayout:
Button:
text: 'Draw Some Stuff'
size_hint: (.2, .1)
pos_hint: {'x': .025, 'y': .025}
on_release: app.root.draw_some_stuff()
DebugPanel:
size_hint: (.2, .1)
pos_hint: {'x': .225, 'y': .025}
Label:
text: str(app.count)
size_hint: (.2, .1)
font_size: 24
pos_hint: {'x': .425, 'y': .025}
<DebugPanel>:
Label:
pos: root.pos
size: root.size
font_size: root.size[1]*.5
halign: 'center'
valign: 'middle'
color: (1,1,1,1)
text: 'FPS: ' + root.fps if root.fps != None else 'FPS:'