반응형

AnimatedSprite나 AnimationPlayer를 사용하지 않고 Sprite만으로도 간단한 애니메이션을 만들 수 있다.

 

Sprite2D를 추가하고 Texture를 지정한다. Hframes는 8, Vframes는 7로 조절한다.

 

Scale을 적절히 조절하고 스크립트도 추가한다.

 

char_blue.png
0.02MB

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using Godot;
 
public partial class Control : Sprite2D
{
    public override async void _Process(double delta)
    {
        if (Input.IsActionJustPressed("ui_accept"))
        {
            // 점프 애니메이션
            for (int i = 32; i < 40; i++)
            {
                await ToSignal(GetTree().CreateTimer(0.2),
                    SceneTreeTimer.SignalName.Timeout);
                Frame = i;
            }
        }
 
        if (Input.IsActionJustPressed("ui_cancel"))
        {
            // 전체 애니메이션
            for (int v = 0; v < Vframes; v++)
                for (int h = 0; h < Hframes; h++)
                {
                    await ToSignal(GetTree().CreateTimer(0.2),
                        SceneTreeTimer.SignalName.Timeout);
                    FrameCoords = new Vector2I(h, v);
                }
        }
    }
}
 

 

위와 같은 코드를 작성한다.

엔터키를 누르면 점프, ESC키를 누르면 전체 애니메이션이 플레이 된다. 애니메이션이 플레이 되는 동안 키를 여러번 동시에 누르면 애니메이션이 겹친다.

 

 

반응형
Posted by J-sean
:
반응형

애니메이션 플레이어를 사용해 보자.

 

씬에 스프라이트를 추가하고 텍스쳐를 지정한다.

 

애니메이션 플레이어를 추가한다.

 

애니메이션 탭에서 Animation 버튼을 클릭하고 New를 선택한다.

 

애니메이션 이름(ani_1)을 지정한다.

 

Add Track - Property Track을 선택한다.

 

Sprite2D를 선택한다.

 

position을 선택한다.

 

애니메이션 탭 - position 트랙에서 마우스 오른쪽 클릭 - Insert Key 선택

 

키가 만들어지면 0초로 옮기고, 스프라이트를 이동해서 키를 하나 더 만들어 1초로 옮긴다.

 

스프라이트에 스크립트를 추가한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
using Godot;
 
public partial class Control : Sprite2D
{
    public AnimationPlayer Player;
 
    public override void _Ready()
    {
        Player = GetNode<AnimationPlayer>("../AnimationPlayer");
    }
 
    public override void _Process(double delta)
    {
        if (Input.IsActionJustPressed("ui_accept"))
            Player.Play("ani_1");
    }
}
 

 

추가된 스크립트에 위와 같은 코드를 작성한다.

 

게임을 실행하고 엔터키를 누르면 애니메이션이 플레이된다.

 

※ 참고

Animation

 

반응형
Posted by J-sean
:
반응형

스프라이트 애니메이션을 만들어 보자.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
import pygame
 
pygame.init()
pygame.display.set_caption("Super fun game development")
screen = pygame.display.set_mode((640480))
clock = pygame.time.Clock()
 
# 이미지 로드 함수.
def LoadImage(path, scale=None, colorkey=None):
    image = pygame.image.load(path).convert()
 
    if scale is not None:
        image = pygame.transform.scale(image, (image.get_width()*scale, image.get_height()*scale))
 
    if colorkey is not None:
        if colorkey == -1:
            colorkey = image.get_at((00))
        image.set_colorkey(colorkey)
 
    return image
 
# pygame.sprite.Sprite 클래스를 상속하는 클래스는 update()를 재정의하고 image, rect 속성을 가져야한다.
class Cat(pygame.sprite.Sprite):
    def __init__(self, position):
        #super(Cat, self).__init__()        
        pygame.sprite.Sprite.__init__(self)
 
        self.elapsedTime = 0
        self.limitTime = 1000/8
        # 1초에 한 사이클. Cat의 Run 동작은 8프레임이다.
        
        self.direction = 1
        self.speed = 4
        self.index = 0
        self.images = [
            LoadImage("Run1.png"0.5-1), LoadImage("Run2.png"0.5-1), LoadImage("Run3.png"0.5-1),
            LoadImage("Run4.png"0.5-1), LoadImage("Run5.png"0.5-1), LoadImage("Run6.png"0.5-1),
            LoadImage("Run7.png"0.5-1), LoadImage("Run8.png"0.5-1) ]
        self.image = self.images[self.index]
        self.rect = self.image.get_rect(center=position)
 
    def flip_image(self):
        self.images = [pygame.transform.flip(image, TrueFalsefor image in self.images]
        # list comprehension
 
    def update(self):
        # 1초에 8프레임 업데이트.
        self.elapsedTime += clock.get_time()
        if self.elapsedTime < self.limitTime:
            pass
        else:
            self.elapsedTime = 0
            self.index += 1
            if self.index >= len(self.images):
                self.index = 0
            self.image = self.images[self.index]
 
def main():
    cat = Cat(position=(screen.get_width()/2, screen.get_height()/2))
    #all_sprites = pygame.sprite.Group(cat)
    all_sprites = pygame.sprite.Group()
    # 스프라이트 오브젝트를 관리하기 위한 컨테이너 클래스
    all_sprites.add(cat)
    # 스프라이트를 삭제할 때는 all_sprites.remove(cat)
    running = True
    
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
                running = False
 
        keys = pygame.key.get_pressed()
        if keys[pygame.K_LEFT]:
            if cat.direction > 0:
                cat.flip_image()
                cat.direction = -1
            cat.rect.move_ip(-cat.speed, 0)
            
        if keys[pygame.K_RIGHT]:
            if cat.direction < 0:
                cat.flip_image()
                cat.direction = 1
            cat.rect.move_ip(cat.speed, 0)
 
        all_sprites.update()
        # 그룹에 속한 모든 스프라이트의 update()를 호출한다.
        
        screen.fill("blue")        
        all_sprites.draw(screen)
        # 그룹에 속한 모든 스프라이트의 image, rect 속성을 이용해 screen에 출력한다.
        # 그룹은 스프라이트의 순서(z-order)를 임의로 정한다.
        pygame.display.flip()
 
        clock.tick(60)
 
    pygame.quit()
 
if __name__ == '__main__':
   main()
 

 

Cat.zip
0.49MB

 

고양이가 자연스럽게 걷고있다.

 

※ 참고

Z-Order에 따라 스프라이트를 그려야 한다면 아래 링크를 확인하자.

LayeredUpdates

This group is fully compatible with pygame.sprite.Sprite.
You can set the default layer through kwargs using 'default_layer' and an integer for the layer. The default layer is 0.
If the sprite you add has an attribute _layer then that layer will be used. If the **kwarg contains 'layer' then the sprites passed will be added to that layer (overriding the sprite.layer attribute). If neither sprite has attribute layer nor **kwarg then the default layer is used to add the sprites.

 

반응형
Posted by J-sean
:
반응형

애니메이션 커브를 사용해 보자.

 

Sphere와 Cube 2개(A지점, B지점)를 생성한다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
 
public class Curve : MonoBehaviour
{
    public Transform targetA;
    public Transform targetB;
 
    public AnimationCurve lerpCurve;
 
    public Vector3 lerpOffset;
    public float lerpTime = 3.0f;
    private float timer = 0.0f;
 
    // Update is called once per frame
    void Update()
    {
        timer += Time.deltaTime;
        if (timer > lerpTime)
        {
            timer = lerpTime;
        }
 
        float lerpRatio = timer / lerpTime;
        Vector3 positionOffset = lerpCurve.Evaluate(lerpRatio) * lerpOffset;
 
        transform.position = Vector3.Lerp(targetA.position, targetB.position, lerpRatio) + positionOffset;
    }
}
 

 

스크립트를 작성한다.

 

작성한 스크립트를 Sphere에 추가한다.

 

TargetA, TargetB를 지정하고 Lerp Curve, Lerp Offset을 수정한다.

 

Lerp Curve는 위와 같이 수정한다.

 

플레이하면 Sphere가 부드럽게 이동한다.

 

반응형
Posted by J-sean
:
반응형

2021.09.25 - [C, C++] - Qt6 설치 및 간단한 사용법

 

Qt의 Pixmap을 이용해 간단한 스프라이트 애니매이션을 구현 할 수 있다.

 

아래와 같은 스프라이트 이미지를 준비 한다. 

horse.zip
다운로드

이 중 처음 11개의 달리는 이미지 프레임만 사용한다.

 

 

Qt Widgets Application으로 기본 설정들을 적용해서 프로젝트를 만든다. (D:\Horse)

 

 

프로젝트를 만든 후 바로 build 디렉토리가 만들어지지 않으므로 build를 한 번 진행 한다.

 

build 디렉토리에 준비한 스프라이트 이미지를 복사한다.

 

mainwindow.h 에 아래와 같이 추가 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include <QMainWindow>
#include <QTimer>
#include <QPainter>
#include <QDebug>
 
namespace Ui {
class MainWindow;
}
 
class Sprite
{
public:
    Sprite(QSize winsize);
    void Draw(QPainter* painter);   // 현재 프레임 그리기
    void NextFrame();   // 다음 프레임 지정
 
private:
    QPixmap* SpriteImage;   // 스프라이트
    int CurrentFrame;   // 현재 프레임
    int TotalFrame; // 전체 프레임 수(11)
    int Column; // 스프라이트내 프레임 열(4)
    int Row;    // 스프라이트내 프레임 행(3) (실제 사용되지는 않음)
    int FramesizeX; // 한 프레임의 X 길이(128)
    int FramesizeY; // 한 프레임의 Y 길이(128)
    QPoint FramePosition;   // 스프라이트내 현재 프레임의 픽셀 단위 위치
    QPoint DrawPosition;    // 윈도우에서 프레임이 그려질 위치
    QSize MainWindowSize;   // 윈도우 사이즈
};
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
 
    virtual void paintEvent(QPaintEvent* event);
 
private:
    Ui::MainWindow *ui;
 
    Sprite* sprite; // 스프라이트
    QTimer* timer;  // 타이머
};
 
#endif // MAINWINDOW_H
cs

 

 

mainwindow.cpp 에 아래와 같이 추가 한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
#include "mainwindow.h"
#include "ui_mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
 
    sprite = new Sprite(this->size());
    timer = new QTimer(this);
    timer->start(50);   // 20Hz 타이머 시작
    connect(timer, SIGNAL(timeout()), this, SLOT(update()));    // timeout() 시그널이 발생하면 update() 실행
    // connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type = Qt::AutoConnection)
    // Creates a connection of the given type from the signal in the sender object to the method in the receiver object.
    // Returns a handle to the connection that can be used to disconnect it later. You must use the SIGNAL() and SLOT() macros
    // when specifying the signal and the method.
}
 
MainWindow::~MainWindow()
{
    delete timer;
    delete sprite;
 
    delete ui;
}
 
void MainWindow::paintEvent(QPaintEvent *event)
{
    QPainter paint(this);   // The QPainter class performs low-level painting on widgets and other paint devices.
    sprite->Draw(&paint);
    sprite->NextFrame();
}
 
// 128X128 사이즈, 4X3배열의 11개 프레임
Sprite::Sprite(QSize winsize) : CurrentFrame(0), TotalFrame(11), Column(4), Row(3), FramesizeX(128), FramesizeY(128), FramePosition(00)
{
    MainWindowSize = winsize;   // 메인 윈도우 사이즈
    qDebug() << "Main Window Size: " << MainWindowSize << endl// Application Output 창에 메인 윈도우 사이즈(400X300) 출력
    SpriteImage = new QPixmap("horse.png"); //The QPixmap class is an off-screen image representation that can be used as a paint device.
    qDebug() << "Sprite Size: " << SpriteImage->size() << endl;    // Application Output 창에 스프라이트 사이즈(512X1024) 출력
 
    // 메인 윈도우 중앙에 프레임 출력
    DrawPosition.setX(MainWindowSize.width() / 2 - FramesizeX / 2);
    DrawPosition.setY(MainWindowSize.height() / 2 - FramesizeY / 2);
}
 
void Sprite::Draw(QPainter* painter)
{
    // Draws a pixmap at (x, y) by copying a part of the given pixmap into the paint device.
    painter->drawPixmap(DrawPosition.x(), DrawPosition.y(), *SpriteImage, FramePosition.x(), FramePosition.y(), FramesizeX, FramesizeY);
 
    // 'Running horse' 문자열 출력
    const QRect rect = QRect(00, MainWindowSize.width(), MainWindowSize.height() / 2);
    painter->setPen(Qt::blue);
    painter->setFont(QFont("Arial"30));
    painter->drawText(rect, Qt::AlignCenter, "Running horse");
}
 
void Sprite::NextFrame()
{
    if (CurrentFrame >= TotalFrame) // 11번 째 프레임을 넘어가면 0으로 초기화
        CurrentFrame = 0;
 
    // 출력할 프레임의 픽셀 단위 위치 지정
    FramePosition.setX((CurrentFrame % Column) * FramesizeX);
    FramePosition.setY((CurrentFrame / Column) * FramesizeY);
 
    CurrentFrame++;
}
cs

 

프로젝트를 build하고 실행하면 아래와 같은 애니매이션이 재생된다.

 

Application Output 창에는 Main Window와 Sprite 사이즈가 표시 된다.

 

iCCP 관련 warning은 무시하거나 이 링크를 참고해서 제거한다.

 

 

반응형
Posted by J-sean
: