You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Term::Animation二维对象碰撞检测异常问题求助

Perl Term::Animation 2.0 二维形状碰撞检测失效修复

问题根源

你的代码存在两个核心问题导致二维碰撞失效:

  1. 动物实体缺少移动逻辑:仅在callback_args中存储了移动方向,但没有实现位置更新的回调,动物无法按照预期移动;
  2. 碰撞后未调整位置:即使触发碰撞反转方向,动物当前位置仍处于碰撞区域,下一帧会继续穿过线条。

修复方案

1. 添加动物移动回调

给动物实体添加update回调函数,根据callback_args中的方向和速度更新位置:

sub update_animal {
    my ($animal, $anim, $delta) = @_;
    my ($x_dir, $y_dir, $z_dir, $speed) = @{$animal->callback_args};
    $speed ||= 1; # 默认移动速度
    my ($x, $y, $z) = @{$animal->position};
    # 根据方向更新X轴位置
    $x += $x_dir * $speed;
    $animal->position([$x, $y, $z]);
}

在add_animal的new_entity调用中添加update参数:

my $animal = $anim->new_entity(
    type => 'animal',
    position => [3, $y, 1],
    shape => $shape,
    color => $mask,
    callback_args => [1, 0, 0, 1], # 最后一个参数设为速度
    die_offscreen => 1,
    collision_handler => \&animal_collision,
    physical => 1,
    update => \&update_animal, # 添加移动回调
);

2. 优化碰撞处理逻辑

在碰撞反转方向后,将动物位置回退一个单位,避免卡在碰撞区域:

sub animal_collision {
    my ($animal, $anim) = @_;
    my $collisions = $animal->collisions();
    foreach my $col_obj (@{$collisions}) {
        if ($col_obj->type eq 'background') {
            my $x_dir = $animal->callback_args->[0];
            # 反转X方向
            $animal->callback_args->[0] = -$x_dir;
            # 回退位置,脱离碰撞区域
            my ($x, $y, $z) = @{$animal->position};
            $x += -$x_dir * 1;
            $animal->position([$x, $y, $z]);
        }
    }
}

3. 验证碰撞掩码

你的mask使用g字符作为碰撞区域标记,符合Term::Animation的要求(非空格字符都会被视为碰撞区域),只需确保mask的形状与shape完全对齐即可。

完整修复后代码

use strict;
use warnings;
use Term::Animation 2.0;
use Term::Animation::Entity;
use Time::HiRes qw(time);
use Data::Dumper;
use Curses;

main();

sub add_animal {
    my ($anim, $y) = @_;
    $y = $y - 5;

    my $shape = q{
                   __
                  / _)
         _.----._/ /
        /         /
     __/ (  | (  |
    /__.-'|_|--|_| };

    my $mask = q{
                   gg
                  g gg
         ggggggggg g
        g         g
     ggg g  g g  g
    gggggggggggggg };

    # Add the animal to the animation
    my $animal = $anim->new_entity(
        type => 'animal',
        position => [3, $y, 1],
        shape => $shape,
        color => $mask,
        callback_args => [1, 0, 0, 1],
        die_offscreen => 1,
        collision_handler => \&animal_collision,
        physical => 1,
        update => \&update_animal,
    );
}

sub update_animal {
    my ($animal, $anim, $delta) = @_;
    my ($x_dir, $y_dir, $z_dir, $speed) = @{$animal->callback_args};
    $speed ||= 1;
    my ($x, $y, $z) = @{$animal->position};
    $x += $x_dir * $speed;
    $animal->position([$x, $y, $z]);
}

sub animal_collision {
    my ($animal, $anim) = @_;
    my $collisions = $animal->collisions();
    foreach my $col_obj (@{$collisions}) {
        if ($col_obj->type eq 'background') {
            my $x_dir = $animal->callback_args->[0];
            $animal->callback_args->[0] = -$x_dir;
            # 回退位置避免持续碰撞
            my ($x, $y, $z) = @{$animal->position};
            $x += -$x_dir * 1;
            $animal->position([$x, $y, $z]);
        }
    }
}

sub add_background {
    my ($anim, $screen_width, $screen_height) = @_;
    my $half_width = int($screen_width / 2);
    my $ground_level = int($screen_height * 0.7);

    for my $y (0..($screen_height)) {
        $anim->new_entity(
            type => 'background',
            shape => ['|'],
            position => [$half_width, $y, 1],
            color => ['w'],
            physical => 1,
        );
    }
}

sub main {
    my $anim = Term::Animation->new();
    $anim->color(1);

    halfdelay(1);

    my $screen_width = $anim->width();
    my $screen_height = $anim->height();
    my $half_width = int($screen_width / 2);
    my $ground_level = int($screen_height * 0.7);

    add_background($anim, $screen_width, $screen_height);
    my $animal = add_animal($anim, ($ground_level - 1));

    # animation loop
    while(1) {
        $anim->animate();

        # use getch to control the frame rate, and get input at the same time.
        my $input = getch();
        if($input eq 'q') { quit(); }
    }

    $anim->end();
}

内容的提问来源于stack exchange,提问作者Sam Dorfman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 21:09:50