Skip to content

Commit

Permalink
flip_jump to main
Browse files Browse the repository at this point in the history
* Updated logic for states.
* Added rotation to Node2D via Python API.
* Fighter directions are based on where there opponents are.
* Add UP and DOWN actions to input buffer.
  • Loading branch information
Chukobyte authored May 21, 2021
1 parent 51a02b0 commit a845c2b
Show file tree
Hide file tree
Showing 13 changed files with 187 additions and 36 deletions.
2 changes: 1 addition & 1 deletion assets/game_projects/fighter/scenes/fight.json
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@
{
"transform2D": {
"position": {
"x": 28,
"x": 32,
"y": 32
},
"scale": {
Expand Down
1 change: 1 addition & 0 deletions assets/game_projects/fighter/src/fight.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def _start(self) -> None:
self.game_properties = GameProperties()
self.frame_counter = 0
self.game_state_manager = GameStateManager()
self.game_state_manager.game_state.frame_states.clear() # TODO: cleanly clear stuff between fights

self._process_game_start_mode()

Expand Down
108 changes: 75 additions & 33 deletions assets/game_projects/fighter/src/fight_simulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,59 @@
from assets.game_projects.fighter.src.hit_box import Attack
from assets.game_projects.fighter.src.input_buffer import InputBuffer
from assets.game_projects.fighter.src.model.animation_name import AnimationName
from assets.game_projects.fighter.src.model.fighter_direction import FighterDirection
from assets.game_projects.fighter.src.model.player import Player
from assets.game_projects.fighter.src.state.game_state import FighterActionState
from assets.game_projects.fighter.src.state.game_state_manager import GameStateManager


class AttackFrameResult:
def __init__(self, collided_entity_ids: list):
self.collided_entity_ids = collided_entity_ids


class AttackManager:
def __init__(self):
self._player_attacks = {}
self._collided_attack_entity_ids = []

def add_attack(self, player: int, attack: Attack) -> None:
self._player_attacks[player] = attack

def _remove_node_active_attack(self, player: int) -> None:
def _remove_node_active_attack(
self, player: int, game_state_manager: GameStateManager
) -> None:
if self.has_attack(player=player):
self._player_attacks[player].queue_deletion()
self._player_attacks[player] = None
player_state = game_state_manager.game_state.player_states[player]
player_state.fighter_action_state = FighterActionState.WAITING

def has_attack(self, player: int) -> bool:
if player in self._player_attacks and self._player_attacks[player]:
return True
return False

def node_has_attack(self, node: Node) -> bool:
return self.has_attack(entity_id=node.entity_id)

def process_frame(self) -> None:
self._collided_attack_entity_ids.clear()
def process_frame(self, game_state_manager: GameStateManager) -> AttackFrameResult:
collided_attack_entity_ids = []
for player in self._player_attacks:
attack = self._player_attacks[player]
if attack:
if not attack.has_hit and attack.has_collided_with_anything():
attack.has_hit = True
self._collided_attack_entity_ids.append(player)
collided_attack_entity_ids.append(player)
attack.frame_life_time -= 1
if attack.frame_life_time <= 0:
self._remove_node_active_attack(player=player)

def get_entity_id_for_collided_attacks(self) -> list:
return self._collided_attack_entity_ids
self._remove_node_active_attack(
player=player, game_state_manager=game_state_manager
)
return AttackFrameResult(collided_entity_ids=collided_attack_entity_ids)


class FightSimulator:
def __init__(self):
self.attack_manager = AttackManager()

def simulate_frame(self, frame: int, game_state_manager: GameStateManager) -> None:
# TODO: Will separate general state processing into more steps once attack damage animations come into play
self._process_general_state(frame=frame, game_state_manager=game_state_manager)

self._resolve_attacks(game_state_manager=game_state_manager)
Expand All @@ -64,29 +71,44 @@ def _process_general_state(
player_state = game_state_manager.game_state.player_states[player_id]
if player_state.input_buffer.is_empty():
player_state.animation_state.set_animation(AnimationName.IDLE)
elif not self.attack_manager.has_attack(player=player_state.id):
for input in player_state.input_buffer.get_frame_inputs(frame=frame):
if input == InputBuffer.Value.LEFT.value:
player_state.node.add_to_position(Vector2.LEFT())
player_state.animation_state.set_animation(AnimationName.WALK)
elif input == InputBuffer.Value.RIGHT.value:
player_state.node.add_to_position(Vector2.RIGHT())
player_state.animation_state.set_animation(AnimationName.WALK)
elif input == InputBuffer.Value.WEAK_PUNCH.value:
weak_punch_attack = Attack.new()
weak_punch_attack.collider_rect = Rect2(x=100, y=32, w=64, h=64)
weak_punch_attack.color = Color(1.0, 0.0, 0.0, 0.75)
player_state.node.add_child(child_node=weak_punch_attack)
weak_punch_attack.frame_life_time = 100
self.attack_manager.add_attack(
player=player_state.id, attack=weak_punch_attack
)
else:
if player_state.fighter_action_state == FighterActionState.WAITING:
for input in player_state.input_buffer.get_frame_inputs(
frame=frame
):
if input == InputBuffer.Value.LEFT.value:
player_state.node.add_to_position(Vector2.LEFT())
player_state.animation_state.set_animation(
AnimationName.WALK
)
elif input == InputBuffer.Value.RIGHT.value:
player_state.node.add_to_position(Vector2.RIGHT())
player_state.animation_state.set_animation(
AnimationName.WALK
)
elif input == InputBuffer.Value.WEAK_PUNCH.value:
weak_punch_attack = Attack.new()
weak_punch_attack.collider_rect = Rect2(
x=32 + (65 * player_state.direction), y=32, w=64, h=64
)
weak_punch_attack.color = Color(1.0, 0.0, 0.0, 0.75)
player_state.node.add_child(child_node=weak_punch_attack)
player_state.fighter_action_state = (
FighterActionState.ATTACKING
)
weak_punch_attack.frame_life_time = 100
self.attack_manager.add_attack(
player=player_state.id, attack=weak_punch_attack
)

def _resolve_attacks(self, game_state_manager: GameStateManager) -> None:
self.attack_manager.process_frame()
for player in self.attack_manager.get_entity_id_for_collided_attacks():
attack_frame_result = self.attack_manager.process_frame(
game_state_manager=game_state_manager
)
for player in attack_frame_result.collided_entity_ids:
# Only resolving damage for now
hp_text_label = game_state_manager.ui_state.hp_labels[player]
opponent_player = game_state_manager.game_state.opponent_player[player]
hp_text_label = game_state_manager.ui_state.hp_labels[opponent_player]
previous_hp = int(hp_text_label.text)
damage = 1 # TODO: Will be based off of attack damage
hp_text_label.text = str(previous_hp - damage)
Expand All @@ -95,3 +117,23 @@ def _update_animations(self, game_state_manager: GameStateManager) -> None:
for player_id in game_state_manager.game_state.player_states:
player_state = game_state_manager.game_state.player_states[player_id]
player_state.animation_state.process_frame()

# Update direction facing
player_one_state = game_state_manager.game_state.player_states[Player.ONE]
player_two_state = game_state_manager.game_state.player_states[Player.TWO]
player_one_position_x = player_one_state.node.position.x
player_two_position_x = player_two_state.node.position.x

if player_one_position_x < player_two_position_x:
player_one_state.direction = FighterDirection.RIGHT
player_one_state.node.flip_h = False
else:
player_one_state.direction = FighterDirection.LEFT
player_one_state.node.flip_h = True

if player_two_position_x < player_one_position_x:
player_two_state.direction = FighterDirection.RIGHT
player_two_state.node.flip_h = False
else:
player_two_state.direction = FighterDirection.LEFT
player_two_state.node.flip_h = True
3 changes: 3 additions & 0 deletions assets/game_projects/fighter/src/hit_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,6 @@ def _start(self) -> None:

def has_collided_with_anything(self) -> bool:
return len(Collision.get_collided_nodes(node=self)) > 0

def get_collided_nodes(self) -> list:
return Collision.get_collided_nodes(node=self)
18 changes: 18 additions & 0 deletions assets/game_projects/fighter/src/input_buffer.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ def __init__(
self,
left_action_name: str,
right_action_name: str,
up_action_name: str,
down_action_name: str,
weak_punch_action_name: str,
frame_limit=12,
):
self._inputs = {}
self.left_action_name = left_action_name
self.right_action_name = right_action_name
self.up_action_name = up_action_name
self.down_action_name = down_action_name
self.weak_punch_action_name = weak_punch_action_name
self._frame_limit = frame_limit

Expand Down Expand Up @@ -66,6 +70,12 @@ def poll_client_inputs(self, frame: int) -> None:
self.add_input(input=InputBuffer.Value.LEFT, frame=frame)
elif Input.is_action_pressed(action_name=self.right_action_name):
self.add_input(input=InputBuffer.Value.RIGHT, frame=frame)

if Input.is_action_pressed(action_name=self.up_action_name):
self.add_input(input=InputBuffer.Value.UP, frame=frame)
elif Input.is_action_pressed(action_name=self.down_action_name):
self.add_input(input=InputBuffer.Value.DOWN, frame=frame)

if Input.is_action_pressed(action_name=self.weak_punch_action_name):
self.add_input(input=InputBuffer.Value.WEAK_PUNCH, frame=frame)

Expand All @@ -77,6 +87,8 @@ def __init__(self, frame_limit=12):
super().__init__(
left_action_name="",
right_action_name="",
up_action_name="",
down_action_name="",
weak_punch_action_name="",
frame_limit=frame_limit,
)
Expand All @@ -90,12 +102,16 @@ def __init__(
self,
left_action_name: str,
right_action_name: str,
up_action_name: str,
down_action_name: str,
weak_punch_action_name: str,
frame_limit=12,
):
super().__init__(
left_action_name=left_action_name,
right_action_name=right_action_name,
up_action_name=up_action_name,
down_action_name=down_action_name,
weak_punch_action_name=weak_punch_action_name,
frame_limit=frame_limit,
)
Expand All @@ -120,6 +136,8 @@ def __init__(self, frame_limit=12):
super().__init__(
left_action_name="",
right_action_name="",
up_action_name="",
down_action_name="",
weak_punch_action_name="",
frame_limit=frame_limit,
)
Expand Down
3 changes: 3 additions & 0 deletions assets/game_projects/fighter/src/model/fighter_direction.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class FighterDirection:
LEFT = -1
RIGHT = 1
26 changes: 26 additions & 0 deletions assets/game_projects/fighter/src/state/game_state.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
from roll.math import Vector2
from roll.node import AnimatedSprite, TextLabel

from assets.game_projects.fighter.src.input_buffer import InputBuffer
from assets.game_projects.fighter.src.model.fighter_direction import FighterDirection
from assets.game_projects.fighter.src.model.player import Player
from assets.game_projects.fighter.src.state.state_data import FrameStateData


class FighterActionState:
WAITING = "waiting"
ATTACKING = "attacking"
GUARDING = "guarding"
HIT_STUN = "hit_stun"
KNOCKED_DOWN = "knocked_down"


class FighterStanceState:
STANDING = "standing"
CROUCHING = "crouching"
IN_THE_AIR = "in_the_air"


class AnimationState:
def __init__(self, node: AnimatedSprite):
self.node = node
Expand Down Expand Up @@ -41,13 +57,23 @@ def __init__(self, id: int):
self.input_buffer = None
self.animation_state = None

self.direction = FighterDirection.RIGHT
self.velocity = Vector2(0, 0)
self.fighter_stance_state = FighterStanceState.STANDING
self.fighter_action_state = FighterActionState.WAITING
self.is_jumping = False


class GameState:
def __init__(self):
self.player_states = {
Player.ONE: PlayerState(Player.ONE),
Player.TWO: PlayerState(Player.TWO),
}
self.opponent_player = {
Player.ONE: Player.TWO,
Player.TWO: Player.ONE,
}
self.frame_states = {}

def poll_input(self, frame: int) -> None:
Expand Down
10 changes: 10 additions & 0 deletions assets/game_projects/fighter/src/state/game_state_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def process_game_start_mode(self, game_start_mode, main: Node) -> None:
player_one_state.input_buffer = InputBuffer(
left_action_name="one_left",
right_action_name="one_right",
up_action_name="one_up",
down_action_name="one_down",
weak_punch_action_name="one_weak_punch",
)
player_one_state.animation_state = AnimationState(
Expand All @@ -62,6 +64,8 @@ def process_game_start_mode(self, game_start_mode, main: Node) -> None:
player_one_state.input_buffer = InputBuffer(
left_action_name="one_left",
right_action_name="one_right",
up_action_name="one_up",
down_action_name="one_down",
weak_punch_action_name="one_weak_punch",
)
player_one_state.animation_state = AnimationState(
Expand All @@ -72,6 +76,8 @@ def process_game_start_mode(self, game_start_mode, main: Node) -> None:
player_two_state.input_buffer = InputBuffer(
left_action_name="two_left",
right_action_name="two_right",
up_action_name="one_up",
down_action_name="one_down",
weak_punch_action_name="two_weak_punch",
)
player_two_state.animation_state = AnimationState(
Expand All @@ -84,6 +90,8 @@ def process_game_start_mode(self, game_start_mode, main: Node) -> None:
player_one_state.input_buffer = OutgoingNetworkInputBuffer(
left_action_name="one_left",
right_action_name="one_right",
up_action_name="one_up",
down_action_name="one_down",
weak_punch_action_name="one_weak_punch",
)
player_one_state.animation_state = AnimationState(
Expand All @@ -103,6 +111,8 @@ def process_game_start_mode(self, game_start_mode, main: Node) -> None:
player_one_state.input_buffer = OutgoingNetworkInputBuffer(
left_action_name="one_left",
right_action_name="one_right",
up_action_name="one_up",
down_action_name="one_down",
weak_punch_action_name="one_weak_punch",
)
player_one_state.animation_state = AnimationState(
Expand Down
8 changes: 7 additions & 1 deletion docs/python_api/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,17 @@ Class used as an interface for scene 2D functionality. Base class for all 2D sc
### Properties

```python
position : roll.math.Vector2
position: roll.math.Vector2
```

Current position of entity.

```python
rotation: float
```

Current rotation in degrees of entity.

---

### Signals
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class AnimatedSpriteRenderingEntitySystem : public EntitySystem {
&currentFrame.drawSource,
&drawDestination,
transform2DComponent.zIndex,
0.0f,
transform2DComponent.rotation,
Color(1.0f, 1.0f, 1.0f),
animatedSpriteComponent.flipX,
animatedSpriteComponent.flipY);
Expand Down
Loading

0 comments on commit a845c2b

Please sign in to comment.