pkulev / xoinvader Goto Github PK
View Code? Open in Web Editor NEWPython space game in terminal
Home Page: http://mmap.me
License: MIT License
Python space game in terminal
Home Page: http://mmap.me
License: MIT License
There is need to replace ugly ncurces code by pygame or pyglet (that implemented at top of pygame), because this is more comfortable and contollable way. The best variant is move ncurces dependent code to separated module or driver (another issue).
This component should be a thin wrapper around enemy ships list with attached animations.
It should call InGame
state's add_object
methods at appropriate times and add enemies with appropriate animations.
Enemies and player must process situation with hp <= 0. At now simple deletion of object or showing Game Over
is enough.
State
handles adding Renderable objects, it should handle and object deletion too.InGameState.remove
method. ✔️ (#60)__del__
for game objects to clean colliders, or reengeneer CollisionManager<->Collider communication solution. ✔️ CollisionManager<->Collider reimplemented.Poetry and pyproject.toml are good things, let's use them.
Withing this issue of later we can get rid of setup.py, requirements.txt, dev-requrements.txt and MANIFEST.in replacing it with just one tiny boi pyproject.toml.
Traceback (most recent call last):
File "game.py", line 82, in
main()
File "game.py", line 78, in main
return game.loop()
File "/home/pkulev/proj/xoinvader/xoinvader/application.py", line 154, in loop
self._state.events()
File "/home/pkulev/proj/xoinvader/xoinvader/teststate.py", line 63, in events
self._events.handle()
File "/home/pkulev/proj/xoinvader/xoinvader/teststate.py", line 43, in handle
self._owner.stop()
AttributeError: 'TestState' object has no attribute 'stop'
Now Ship has _direction
(int) and _dx
, x
pos is calculated as x + _direction * _dx
.
https://github.com/pkulev/xoinvader/blob/master/xoinvader/ship.py#L194-L219
Ship.update
method to unify itBackground is the battlefiled, on which the action is taking place.
It will scroll from top to bottom with some speed, that may vary.
Background may consist of several looping chunks, to reduce efforts of creating really long scrolls, and also to adapt to varying battle times (e.g. boss fights).
It can be loaded from file or it chunks may be generated proceudrally.
Mechanism to loop through chunks and to smoothly traverse from one chunk another must be implemented.
Background has the lowest Z-level, everything should be drawn on top of it.
Background should have a mechanism to change its speed of top-down-scrolling.
And maybe in other places too.
Measuring real time in things like animation or status update can cause problems, since it's a major side effect, which we can't turn off.
So we can't implement things like pause, because we can't stop global timer.
Also we can't change animation speed just by altering timer threshold.
Current alternative is presented in the background module, which counts number of times update
function was called. This is stub implementation, however.
We should really think about how to decouple Timer
and real time measuring.
However, real time measurements of course should be resent somewhere, but ideally there should be only one place, both logically and in sense of source code, where this happens.
Alpha milestone mentions following things:
While not all of them have to have an editor, we must decide on how to create, use and modify all of them, in terms of in-game engine entities.
Background tries to render on border (I've learned it from renderer logs).
This is not quite good because:
Find place where Surface sends to renderer and don't use (x, 0) and (x, Settings.layout.border.y) coordinates.
We live in the terminal, and thus we may run console commands and check env
and whatnot.
Thus we (probably) can install font that supports unicode and even colorful emojis.
It might be quite funny.
Also we may even launch a separate terminal of our choise and provide it with all necessary fonts.
∧_∧ / ̄ ̄ ̄ ̄ ̄
( ´∀`)< オマエモナー
( ) \_____
│ │ │
(__)___)
Annoying bug.
We need file format to keep 3 things together:
style
parameter of Surface)Application itself must register colors and attributes it uses or just use defaults.
[meta]
name = "kekstrel"
[layers]
image = """
O
<=H=>
* *
"""
# w is white
# b is blue
color = """
w
wwwww
b b
"""
# n is normal
# B is bold
attr = """
n
nnnnn
B B
"""
Add classmethod Surface.from_file(). Replace hardcoded images with files, set path to them in corresponding configs.
Implement mechanism for creating declarative animations.
Keep in mind further implementing of animation editor.
It's ok when weapon cannon consists from one character ( "*" or "^").
Weapon.make_shot(); Render.render_all().
on events choose direction, on update - update position.
This can be accessed via chain of event handler calls.
Menu was created couple years ago, and now we need to improve it to work with menu hierarchies using that multimedia framework activities we have. This also needed for using scoreboard.
Menu structure is a tree, each it's node can provide command override (e.g. as classfield).
$ xoinvader
Split checks in render into 2:
This will prevent errors while resizing terminal.
I've made several changes towards cross-python compatibility, but I've stuck with unicode passing to curses add* functions.
Need to fix this.
Now we have awful zaichatkie of logging.
Read about logging module and do it properly, with configuring root logger and getting it when needed as well as subsystem loggers.
We can provide some sort of input abstraction and reuse it for both backends.
Instead of getting raw key and making all math in game objects' update methods we can use something like Input.axis(...).
separate update and render calls
PeriodicalCallback.init now doesn't expect ioloop as parameter.
Also briefly scrolling of ioloop module I saw that some things I use in application package are deprecated by v5. Upgrading tornado will be in separate issue, here we can just lock tornado version.
Remove old renderer and use render_objects
function for weapons' bullets.
Current implementation is naïve -- it just checks all possible collisions between all possible objects, traversing the list of registered collision combinations and then objects of corresponding types.
This is complex in a cyclomatic way. Thus it needs to be improved. The idea is to make use of some logarithmic data structure, like BSP-tree or quadtree to divide objects and to decrease number of unnecessary checks, and to get rid of that horrible deep loop nesting.
Weapon shells (bullets, charges, beams) must be separate GameObject with separate _image property. Weapon can have own _image for example.
make view_cov
just hangs at
xoinvader/tests/test_application.py::test_curses_application
line.
Happened 4 times to me.
This
time not came yet, but some changes we need to do in order to unblock further development.
We need to design and implement as simple as possible object system that will fit to our needs for this time.
We have ships and backgrounds that have Surface to render. We have projectiles that looks like not good designed lists of coordinates...
GameObject
is recursive data structure that have position in world coordinates if it is not part of another object, otherwise it has local coordinates as offset to parent object.GameObject
can hold subobjects of type GameObject
with own colliders, animations, sprites.State
as implemented now is a scene, that holds game objects and does all interations with them in loop when this state is active.State
can hold many instances of GameObject
.Application
holds one or more State
s and executes current state.State
can be used in one time.IOLoop
3 methods of current State
object: events()
, update(dt)
, and render()
.State
has it's own message queue to provide messaging between objects and maybe even whole engine subsystems.Application
can have dedicated queue for something.GameObject
can put event into queue via it's State
.EventHandler
of State
pops all events from queue and process them on events
stage of the State
.class PlayerShip(Renderable):
def __init__(self, ...):
self.add(WeaponSlot(transform, ...)) # transform is offset
self.add(WeaponSlot(transform. ...)) # that applied to PlayerShip's transform
self.add(LeftEngine(transform, ...)) # maybe some form of declarative definition
class InGameState(State):
def __init__(self, owner):
self._actor = PlayerShip(transform, ... )
self.add(self._actor) # will be rendered and updated
class InGameEventHandler(Handler):
def handle(self):
# after input
for event in self.owner.get_events():
if event.recepient:
self.dispatch(event)
# some other things
class Weapon(Renderable):
def update(self):
if self._fire:
self.add(Projectile(self))
...
class RocketProjectile(...):
def on_trigger_enter(collider):
if self.side != collider.game_object.side and collider.game_object.side != "obstacles":
collider.game_object.apply_damage(self)
...
Looks like only 3.6+ test env affected.
Travis logs: https://travis-ci.org/pkulev/xoinvader/jobs/350040312
Now configs are read only, no need to create machinery to mantain wierd cases.
Now config (Settings) creates on import time, with some singletones raises problem of depending on import order.
Solution:
common
We have animations, we need to interpolate values. There are two common methods: linear equation and curve-based interpolation. As simpler and more appropriate for ASCII GAME ENGINE we used to implement linear interpolation.
Empirical formula is:
interpolated = value1 + (value2 - value1) / (time2 - time1) * (current_time - time1)
Where:
For now it's enough to implement interpolation for next types
Split Animation.update method to two for interpolated and fixed (discrete) animation.
This needed due to different approaches to animation keyframe switching and will too
complicate code to keep single codepass.
I fucked up. To check if module has attribute we need to except AttributeError, not ImportError.
Just replace.
#While working on #19, I found myself in a dire need of logging facility.
I was debugging filter/list issue, and ncurses are not very friendly towards output that bypasses their own methods.
So I naturally did something like this:
with open("debug.log", "w") as f:
...
print("List:", l, file=f)
...
print("More info", info, file=f)
And so on. It was quite useful, but it's tedious to write this all by hand, and even more tedious to clean it all up. So I thought that we might benefit from having our own logging facility.
In fact, I'm surprised that we don't have one yet.
UPD. It turned out we have logging after all. It's in utils.py
. Whatever, then we need to have a wiki article on it.
Blocked_ by #31
Good combines with implementing some sort of projects
.
sorting layers
:Settings.project.sorting_layers = [
"GUI",
"Player",
"Enemies",
"Pick-ups",
"Background"
]
# Somewhere in game classes
class Background(Renderable):
sorting_layer = "Background"
...
"default"
is initial value for every untagged object.
This is unity-like alternative to Renderable.render_priority with meaningfulness instead of strange integers.
Maybe a little difficult to implement sorting, need some kind of prototyping.
@taptap, let's discuss it.
class ERocketLauncher(Weapon):
super(ERocketLauncher, self).__init__(**CONFIG[self.type])
self._collider = SphereCollider(pos=Vec3(0, 0), radius=1)
...
Further collision detection code will be moved into engine and colliders will turn into components for lightweight entities.
Collision detection tightly coupled with event system
and object system
.
Debugging console or some kind of tool for interacting with engine and showing current state.
All curses applications behave by such manner. Need some R&D for workarounding.
Now background has it's own format, but with texture we can unify it. It will be needed later for level description file format and also we will be able paint background with different colors.
Maybe we can even use chunks from several background files at once.
[meta]
name = "small town"
[chunk.bridge]
image = """
\ /
| |
| |
| |
/ \
"""
color = """
"""
attrs = """
"""
[chunk.something]
image = """
^^^^^^ ^^^^^^ ^^^^^^
00 000 000 000 00 00 0
"""
Replace old parser or add new one.
Add travis build for next versions:
I struggle with how to run development version for 15 minutes now.
Nothing I have tried had worked. Please send help.
ncurses-5.9 works well, but segfaults with version 6.
Looks like all broken here: 8b1d6c2.
Previous commit works well on python2.7.11 and python3.5, but this only with python2.7.11.
Traceback: ... > curses.cbreak()
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.