이 함수 다른 js 파일에서 쓰려고 export 붙였더니 오류 남.
이유 : App.js에 있는 상태 변수를 갱신하는 함수라서 그렇다고 함
에서 playerTouchingDoor이 true면
threadButtonPressed() 함수가 팝업창을 띄우게 하고 싶은데,그러려면 app.js에 있는 showPopup()을 호출해야되잖아? 이거 어떻게 해야되는거임 export도 안되면
Phaser와 React를 함께 사용하는 경우, React의 컴포넌트 상태나 함수를 Phaser 코드에서 호출하는 방법은 모듈 시스템만으로는 해결되지 않습니다. Phaser는 React 컴포넌트 트리와 별도의 동작을 하기 때문에 직접적으로 React의 함수를 호출할 수 없으므로, 상태 관리 시스템(예: Redux, Context API)나 콜백 함수 전달 방식을 통해 이 문제를 해결할 수 있습니다.
Phaser 코드에서 React의 상태를 조작하거나 React 함수를 호출하려면 React 컴포넌트에서 Phaser로 함수를 전달해주거나, 전역 상태 관리 솔루션을 사용해 통신해야 합니다.
다 뭔가 굳이 이렇게까지 해야되나 싶어서 o1-preview 사용
Solution 1: Pass a Callback Function from React to Phaser
One of the simplest and most effective methods is to pass a callback function from your React component (App.js) to your Phaser game (phaser.js). This way, when the event occurs in Phaser, you can call this callback function, which will execute showPopup() in your React component.
Solution 2: Use a Custom Event
If you prefer not to pass callbacks, another straightforward approach is to use custom events to communicate between Phaser and React.
Solution 3: Use a Global Function
As a last resort, you can define showPopup() on the global window object so that it can be accessed from anywhere.
Recommendation:
I recommend Solution 1 (passing a callback function) as it is the most straightforward and aligns well with React's component-based architecture. It avoids global variables and keeps your code organized.
Additional Notes:
Ensure Proper Scope: When passing the callback, make sure it has access to the necessary scope and state within your React component.
Cleanup: If you're adding event listeners or global functions, ensure you clean them up in your useEffect cleanup function to avoid memory leaks.
Modularization: Keep your phaser.js and App.js modular by only exposing what is necessary for communication.
useEffect(() => {
if (isCanvasVisible && canvasRef.current && !phaserInitialized) {
initializePhaserGame('phaser-container', showPopup);
// ... set phaserInitialized, etc. ...
}
}, [isCanvasVisible, canvasRef, phaserInitialized]);
역시나 나를 실망시키지 않았다.
그냥 함수를 인자로 주면 됐던 거임.
게시판 올릴 팝업창 구현 완료.
{/* 팝업창이 보이는 경우에만 렌더링 */}
{isPopupVisible && (
<div className="popup-overlay">
<div className="popup">
<h2>팝업창</h2>
<p>이 팝업은 특정 이벤트에 의해 표시됩니다.</p>
<button onClick={closePopup}>닫기</button>
</div>
</div>
)}
/* 배경 어둡게 처리 */
.popup-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
}
이거 뭐 어떤 원리로 배경이 어두워지는 건지 이해가 안되는데
이 CSS 코드에서 배경이 어두워지는 원리는 rgba(0, 0, 0, 0.7)
와 position: fixed
, z-index
속성을 결합하여 화면 전체에 어두운 반투명 레이어를 덮는 방식으로 동작합니다. 하나씩 원리를 설명해 드리겠습니다.
position: fixed
와 화면 전체 덮기position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
position: fixed
: 요소를 화면에 고정합니다. 이 속성은 뷰포트(viewport)에 대해 상대적으로 위치하게 하므로, 페이지를 스크롤해도 해당 요소가 화면에 고정되어 움직이지 않습니다.top: 0; left: 0;
: 요소가 화면의 왼쪽 상단 모서리(좌표 0, 0)에서 시작하도록 위치를 설정합니다.width: 100%; height: 100%;
: 이 요소가 화면의 전체 너비와 높이를 덮도록 설정합니다. 결과적으로 이 요소는 페이지의 전체 영역을 덮는 레이어가 됩니다.rgba(0, 0, 0, 0.7)
background-color: rgba(0, 0, 0, 0.7);
rgba(0, 0, 0, 0.7)
는 반투명한 검정색입니다.rgb(0, 0, 0)
는 검정색을 나타내고,0.7
은 투명도를 나타내는 알파 값입니다. 이 값은 0(완전 투명)에서 1(완전 불투명) 사이의 값을 가질 수 있으며, 0.7
은 70% 불투명하고 30% 투명한 상태를 의미합니다.따라서, 화면에 이 검정색 레이어가 덮이지만 투명도가 적용되기 때문에 배경이 어둡게 보입니다.
display: flex;
justify-content: center;
align-items: center;
display: flex
: flexbox
레이아웃을 사용하여 자식 요소를 중앙에 배치할 수 있도록 합니다.justify-content: center
: 수평 방향으로 자식 요소를 중앙에 배치합니다.align-items: center
: 수직 방향으로 자식 요소를 중앙에 배치합니다.이 설정은 팝업이 화면의 중앙에 표시되도록 해줍니다.
z-index
로 요소의 순서 제어z-index: 1000;
z-index
: 요소의 쌓임 순서를 정의합니다. 값이 클수록 화면의 앞쪽에 나타납니다.z-index: 1000
을 사용하여 이 레이어가 다른 콘텐츠 위에 쌓이도록 설정하고, 화면의 가장 앞쪽에 배경을 덮는 검정색 오버레이를 표시합니다.리액트는 포트 3000에 열려있고 node.js는 포트 5000에 열려있음. 그냥 이대로 실행하면 원래 알아서 연결되는게 정상이야? 내가 뭐 따로 해주는거 없어도?
React 애플리케이션이 포트 3000에서 실행되고, Node.js 서버가 포트 5000에서 실행되고 있을 때, 브라우저에서 크로스 오리진 리소스 공유(CORS) 문제가 발생할 수 있습니다. 기본적으로 포트 3000과 포트 5000은 다른 오리진으로 간주되기 때문에, 서버에서 CORS 설정을 해주어야 React에서 Node.js 서버로 데이터를 요청할 수 있습니다.
나온다. 나이스.
cors 지정할 때 3000같은 거 안 적었잖음. 이렇게 되면 신뢰 못하는 놈들도 마구 들어오는거 아님?
뭔가 이상하다 했다.
근데 포트만 한정하면 어차피 의미 없는거 아님? 브루트포스로 다 확인해보면 그만이잖아. 의미 없어서 그냥 포트 지정 안 했던거임? 현업에선 어떻게 보안 강화함?
맞습니다. 포트만 한정하면 보안에 큰 의미가 없는 이유는, 포트는 누구나 쉽게 알 수 있고, 브루트포스 등으로 추측할 수 있기 때문입니다. 단순히 포트나 도메인만으로 보안을 강화하기엔 한계가 있으며, 더 나은 보안 전략이 필요합니다.
현업에서는 CORS 설정을 포함해, 여러 보안 기법을 결합해 서버와 API의 보안을 강화합니다. 다음은 현업에서 API 보안을 강화하는 여러 방법들입니다.
CORS는 기본적으로 웹 애플리케이션이 신뢰할 수 있는 오리진(도메인, 프로토콜, 포트)에서만 데이터를 요청하도록 설정할 수 있습니다. 하지만 이는 보안의 첫 단계일 뿐이며, 다음과 같은 추가적인 방법들이 필요합니다.
서버는 단순히 오리진을 신뢰하는 것 외에도, 사용자가 누구인지 확인하고, 그들이 해당 API에 접근할 권한이 있는지를 검증해야 합니다.
보안까지 신경쓰면 좋겠지만 지금은 일단 넘어가자.
관계형 db 쓰는 곳과 비관계형 db쓰는 곳의 차이
그냥 몽고db 쓰면 될듯?
이 코드는 Mongoose 라이브러리를 사용하여 MongoDB에서 게시글(Post) 데이터를 저장할 구조(스키마)를 정의하고, 그 스키마를 기반으로 모델을 생성하는 과정입니다. Mongoose는 MongoDB에서 데이터를 구조적으로 다루기 쉽게 해주는 라이브러리로, 데이터 스키마를 통해 데이터를 어떻게 저장하고, 다룰지 정의할 수 있습니다.
postSchema
)const postSchema = new mongoose.Schema({
title: String,
content: String,
});
mongoose.Schema
: MongoDB에 저장될 데이터의 구조를 정의하는 객체입니다. 여기서 postSchema
는 게시글을 나타내며, 게시글은 두 개의 필드(title
, content
)를 가집니다.title: String
: 게시글의 제목을 저장하는 필드로, 데이터 타입은 String
입니다.content: String
: 게시글의 내용을 저장하는 필드로, 데이터 타입은 String
입니다.이 스키마를 통해 Mongoose는 MongoDB에 데이터를 삽입할 때 해당 구조에 맞춰 데이터를 저장하도록 강제합니다. 즉, 게시글을 저장할 때는 항상 제목과 내용이 필요하고, 각각 문자열이어야 한다는 것을 정의하는 것입니다.
Post
)const Post = mongoose.model('Post', postSchema);
mongoose.model
: 스키마를 바탕으로 MongoDB와 상호작용할 수 있는 모델을 생성하는 함수입니다. 모델은 MongoDB의 컬렉션(collection)과 연결됩니다.Post
: 이 모델은 MongoDB에서 'posts'라는 컬렉션과 연결됩니다. Mongoose는 자동으로 모델 이름의 복수형(Post
→ posts
)으로 컬렉션을 만듭니다. 이제 이 Post
모델을 사용하여 MongoDB에서 게시글을 조회하거나, 저장하거나, 수정하거나, 삭제할 수 있습니다.이 Post
모델은 MongoDB의 posts
컬렉션에 있는 데이터를 다루기 위한 인터페이스 역할을 합니다.
그니까
형식이 스키마고
db에 저장된건 컬렉션이고
그거 두 개 묶은 건 모델이고
내가 써야될 건 모델이다
데이터 올라가기 시작
// 게시글 목록 가져오기 (GET)
app.get('/api/posts', async (req, res) => {
try {
const posts = await Post.find(); // MongoDB에서 모든 게시글 가져오기
res.json(posts);
} catch (err) {
res.status(500).json({ error: 'Server error' });
}
});
이거 설명 좀
이 코드는 Node.js와 Express를 사용하여 MongoDB에서 게시글 목록을 가져오는 API 엔드포인트입니다. 아래는 각 부분의 설명입니다.
// 게시글 목록 가져오기 (GET)
app.get('/api/posts', async (req, res) => {
app.get('/api/posts', ...)
:Express
의 GET
요청 핸들러로, /api/posts
라는 경로로 들어오는 GET 요청을 처리합니다. 예를 들어, 클라이언트가 /api/posts
URL로 요청을 보내면 이 핸들러가 실행됩니다.async (req, res)
:MongoDB
에서 데이터를 가져오는 작업은 시간이 걸리기 때문에 async
를 사용하여 비동기적으로 처리하고 있습니다.req
: 요청(request) 객체로 클라이언트가 서버로 보낸 요청에 대한 정보를 담고 있습니다.res
: 응답(response) 객체로 서버가 클라이언트에게 응답을 보낼 때 사용합니다.try {
const posts = await Post.find(); // MongoDB에서 모든 게시글 가져오기
res.json(posts);
} catch (err) {
res.status(500).json({ error: 'Server error' });
}
try { ... } catch (err) { ... }
:
try...catch
구문을 사용하여 에러 처리를 하고 있습니다.const posts = await Post.find();
:
Post.find()
: MongoDB에서 모든 게시글을 가져오는 작업입니다. Post
는 MongoDB의 모델로, 보통 Mongoose
라는 라이브러리를 사용해 정의된 스키마를 가리킵니다. find()
는 MongoDB의 쿼리 메서드로, 이 모델의 모든 문서를 검색하여 결과를 반환합니다.await
: 이 작업은 비동기적이기 때문에 await
키워드를 사용하여 데이터베이스에서 결과가 반환될 때까지 기다립니다.res.json(posts);
:
posts
는 MongoDB에서 가져온 게시글 데이터의 배열입니다.catch (err)
:
/api/posts
경로로 GET 요청을 보냅니다.Post.find()
메서드를 사용합니다.이 코드는 주로 REST API 서버에서 게시글 목록을 클라이언트에게 제공하는 기능을 담당하며, 클라이언트는 이를 통해 게시글 데이터를 받아올 수 있습니다.
회원가입 로그인까지 구현하는 건 굳이굳이의 영역인 것 같아서
간단하게 닉네임만 입력할 수 있게 할 것.
{/* 로그인 여부에 따라 다른 콘텐츠 렌더링 */}
{!isLoggedIn ? (
<div className="login-form">
<h2>닉네임 입력</h2>
<input type="text" placeholder="아이디" className="login-input" ref={nicknameRef} />
<button className="login-button" onClick={handleLogin}>
확인
</button>
</div>
) : (
<>
<h2>게시판</h2>
{loading ? (
<p>로딩 중...</p> // 로딩 중일 때 표시
) : (
<div className="post-list">
{posts.map((post) => (
<div key={post.id} className="post-card">
<div className="post-header">
<div className="post-user-info">
<h3 className="post-title">{post.title}</h3>
<p className="post-username">@username</p> {/* 사용자명 */}
</div>
</div>
<p className="post-content">{post.content}</p>
</div>
))}
</div>
)}
<button onClick={closePopup}>닫기</button> {/* 하단 닫기 버튼 */}
</>
)}
조건에 따라 렌더링 다르게
// 로그인 버튼을 눌렀을 때 로그인 상태로 전환
const handleLogin = () => {
const nicknameValue = nicknameRef.current.value; // input 필드의 값을 가져옴
setNickname(nicknameValue); // 닉네임 상태 업데이트
setIsLoggedIn(true); // 로그인 상태로 전환
console.log('닉네임:', nicknameValue);
};
확인 누르면 서버에 보낼 닉네임 설정하는 핸들러
// 닉네임을 가져옴
const nicknameInput = document.getElementById('nickname').value;
const nickname = nicknameInput ? nicknameInput.trim() : 'Jungler'; // 공백 제거 후 값 확인
// 입력 필드의 데이터를 가져옴
const inputField = document.getElementById('player-input');
const content = inputField ? inputField.value.trim() : ''; // 공백 제거 후 값 확인
이전에 했던 거랑 똑같이 요소로 가져오면 되겠거니 했는데
그러면 안됐음. 닉네임 입력하는 요소는 이미 입력 버튼 누르면 사라진 뒤이기 때문.
그럼 그 정보를 저장할 수 있는 건 어디에 있느냐? 라고 하면
usestate인가 props인가 뭔가 하는 이상한 얘기를 또 들이밈.
저번에는 그냥 함수를 인자로 넘겨주는 방식으로 해결했는데
이건 phaser가 초기화된 이후에 데이터가 입력되는거라 그런 식으로 얼렁뚱땅 해결도 불가능.
데이터를 어딘가에 저장(브라우저 캐시? 투명한 div의 data 필드?)하면 억지로 해결할 수는 있겠지만
일단 gpt가 권장하는 정석적인 방법 시도해보기.
뭔지 잘 이해가 안 가서 클로드한테 좀 그려달라고 함.
그림이 좀 이상하게 그려지긴 했지만 어쨌든 대충 이해했다.
props : 부모가 자식한테 값(보니까 함수도 되는듯?) 주는거
자식은 그냥 부모한테 데이터 반환할 수 있는듯.
import React, { useRef } from 'react';
function Child({ onLogin }) {
const nicknameRef = useRef(null); // 입력 필드를 참조하기 위한 ref
const handleButtonClick = () => {
const inputValue = nicknameRef.current.value; // 입력된 값을 가져옴
onLogin(inputValue); // 부모에게 값을 전달함
};
return (
<div>
<h2>자식 컴포넌트</h2>
<input type="text" ref={nicknameRef} placeholder="닉네임" />
<button onClick={handleButtonClick}>로그인</button>
</div>
);
}
export default Child;
자식 컴포넌트 이렇게 만들어놓고 호출하면 함수 실행도 하면서 html 요소도 만들어지는 거인듯
아 근데 너무 복잡한거 같아서 다시 물어봄
버튼 누르는 건 phaser.js에서 이루어지기 때문에 app.js에서 인자를 넘겨주는 식으로는 안된다고 생각했음. 혹시 js도 python마냥 레퍼런스만 주는거라서 phaserinitialize 할 때 인자 넘겨줘도 나중에 업데이트 되는게 반영되는거야?
JavaScript에서도 참조(Reference)가 중요한 개념입니다. Python처럼 객체는 참조로 전달되기 때문에, 객체나 배열 같은 참조형 데이터는 다른 곳에 전달한 후에 값이 변경되면 그 변경이 반영됩니다. 따라서 Phaser 초기화 시 인자로 객체를 넘겨주는 방식으로 진행하면, 이후에 그 객체의 값을 업데이트하면 반영이 됩니다.
아 그냥 인자로 줘도 되는거였는듯
아니 인자로 주니까 업데이트가 안되는데?
네, 말씀하신대로 JavaScript에서 인자를 넘길 때 그 인자가 객체나 배열 같은 참조형 데이터라면 참조가 전달되지만, React의 상태(useState)는 비동기적으로 동작하므로, 넘길 때 값이 즉시 업데이트되지 않을 수 있습니다. 이는 React에서 상태가 바로 업데이트되지 않는 특성 때문입니다.
즉, 인자로 객체를 넘겨주는 방식은 React에서 상태의 비동기 업데이트 때문에 Phaser와 잘 맞지 않을 수 있습니다.
야이 씨...
ref를 인자로 넣어도 안됨. ref는 이미 사라지고 없기 때문임. 차라리 브라우저에 값 저장해놓는 방식 쓰고 싶은데 그건 어떰?
브라우저에 값을 저장하는 방식은 좋은 대안이 될 수 있습니다. 브라우저의 로컬 저장소(Local Storage) 또는 세션 저장소(Session Storage)를 사용하면, 리액트 컴포넌트가 언마운트되거나 페이지가 새로고침돼도 데이터를 유지할 수 있습니다.
Local Storage:
데이터를 브라우저에 영구적으로 저장합니다. 사용자가 페이지를 새로고침하거나 브라우저를 닫아도 데이터가 유지됩니다.
Session Storage:
데이터를 세션 동안만 저장합니다. 즉, 브라우저 탭을 닫으면 데이터가 사라집니다.
이거면 될듯.
시작할 때 세션 스토리지 지워서 초기화하고 싶은데 가능?
네, 시작할 때 Session Storage를 초기화하는 것은 가능합니다. 컴포넌트가 처음 렌더링될 때 또는 앱이 시작될 때 sessionStorage.clear()를 호출하면 저장된 모든 세션 데이터를 삭제할 수 있습니다.
// 컴포넌트가 처음 렌더링될 때 세션 스토리지 초기화
useEffect(() => {
sessionStorage.clear(); // 세션 스토리지 초기화
}, []); // 빈 배열을 넣어 컴포넌트가 처음 마운트될 때만 실행
구현 완료.
켜놓고 몇 초 지나면 노트북 팬이 웽 하고 돌아가기 시작함.
오래 켜놓으면 생성이 점점 빨라지기도 함. 뭔가 문제가 있는게 분명.
useEffect(() => {
if (isCanvasVisible)
setInterval(createLetter, 100); // 0.1초마다 새로운 글자를 생성
}, [isCanvasVisible]); // 캔버스가 표시될 때만 실행
일단 이렇게 하면 여러 번 인터벌 되는 건 막아질거라고 해서 적용하긴 했음.
이후 모니터링 해보니까 생성 빨라지는 현상은 해결
useState는 app.js 말고 다른 데에서는 못 쓰는건가?
useState는 React의 훅(hook) 중 하나로, React 컴포넌트 내에서만 사용할 수 있습니다. React 훅은 컴포넌트의 상태를 관리하는 데 사용되기 때문에, React 함수형 컴포넌트 내에서만 호출할 수 있으며, 그렇지 않은 파일이나 함수에서는 사용할 수 없습니다.
따라서 useState를 React 컴포넌트가 아닌 다른 곳에서 사용하려면 몇 가지 제한이 있습니다. 예를 들어, Phaser.js와 같은 파일에서 useState를 직접 사용할 수는 없지만, 몇 가지 방법으로 React 컴포넌트와 연동할 수 있습니다.
다른 js 파일에서 function app 선언하면 걔도 컴포넌트 되는거임?
리액트에서 컴포넌트는 일반적으로 React 함수형 컴포넌트 또는 클래스형 컴포넌트로 정의됩니다. 즉, 단순히 자바스크립트 파일에 함수를 선언한다고 해서 그 함수가 리액트 컴포넌트가 되는 것은 아닙니다. 리액트 컴포넌트가 되기 위해서는 그 함수가 React의 규칙을 따라야 합니다.
리액트 컴포넌트는 기본적으로 다음의 두 가지 특징을 충족해야 합니다:
useState
, useEffect
등의 리액트 훅을 사용할 수 있습니다.import React from 'react';
function App() {
return (
<div>
<h1>Hello, World!</h1>
</div>
);
}
export default App;
이 코드는 리액트 컴포넌트입니다. 이 컴포넌트는 JSX(<div>...</div>
)를 반환하며, 리액트 생명주기에서 관리됩니다.
function someFunction() {
return "Hello, World!";
}
위 코드는 자바스크립트의 일반적인 함수로, 이 함수는 리액트 컴포넌트가 아닙니다. 단순히 문자열을 반환하며 리액트의 JSX 또는 리액트 훅과는 아무런 연관이 없습니다.
리액트에서는 여러 자바스크립트 파일에 컴포넌트를 선언하고 사용할 수 있습니다. 각 파일에 함수를 선언할 때 그 함수가 JSX를 반환하고 React 훅을 사용할 수 있는 구조로 만들어야만 컴포넌트로 인식됩니다.
SomeComponent.js
import React from 'react';
function SomeComponent() {
return (
<div>
<p>This is SomeComponent</p>
</div>
);
}
export default SomeComponent;
App.js
import React from 'react';
import SomeComponent from './SomeComponent';
function App() {
return (
<div>
<h1>Main App Component</h1>
<SomeComponent />
</div>
);
}
export default App;
여기서 SomeComponent.js
에 선언된 함수는 JSX를 반환하므로 리액트 컴포넌트로 인식됩니다. App.js
에서 SomeComponent
를 불러와 사용할 수 있습니다.
일반 함수는 다음과 같은 특징을 가집니다:
useState
, useEffect
등)을 사용할 수 없습니다.반면, 리액트 컴포넌트는:
리액트 컴포넌트를 쓰는 건 조금 복잡해질수도
원래대로라면 제일 간단한 길이었겠지만
페이저까지 같이 쓰다보니 뭔가 꼬여버릴 것 같음.
일반 자바스크립트 파일에서
import { fireBullet } from "./component/bulletUtils";
이런거 하면 쓸 수는 있는거지?
일반 자바스크립트 파일에서도 ES6 모듈을 사용하여 import와 export를 통해 다른 파일에서 함수를 불러와 사용할 수 있습니다.
아 이러면 그냥 페이저에서 let으로 intervalid 관리하면 될듯?
끄아악 적들의 습격이다
토글이 안됨
해당 함수에서 변수 값을 바꾸니까 intervalid가 갱신이 안돼서 그랬음.
return으로 반환하고 phaserGame.js에서 갱신시키니까 해결.
if (playerTouchingSwitchButton) {
intervalId = switchButtonPressed(switchButton, intervalId); // 인터벌 ID 저장
}
PhaserGame.js
export const switchButtonPressed = (switchButton, intervalId) => {
console.log("switchButton");
if (intervalId) {
clearInterval(intervalId); // 인터벌 제거
} else {
const newIntervalId = setInterval(createLetter, 100); // 0.1초마다 새로운 글자를 생성
return newIntervalId; // 인터벌 ID 반환
}
}
buttonUtils.js
문자 토글 구현 완료.
대충 해야할 거 구현은 다 끝낸 것 같고,
나머지는 폴리싱인데
사소한 것들은 웬만하면 다 다듬었고
남은 큰 것들은
스위치 토글하면 대포도 올라오는 연출,
페이저 시작되면 우주선 불꽃 나오는 연출
버튼 누르는 애니메이션, 스위치 누르는 애니메이션, 문 들어갔다가 나오는 애니메이션
AWS 올리기
정도가 있을듯
근데 생각해보니 귀찮. 할만큼 한듯?
키 누르면 눌렸다가 다시 올라오는 코드 추가.
if (playerTouchingDeleteButton) {
deleteButton.setTexture('delete_pressed'); // 눌림 이미지로 변경
setTimeout(() => deleteButton.setTexture('delete'), 200); // 200ms 후 원래 이미지로 복구
const textarea = inputField.node.querySelector('textarea');
textarea.value = textarea.value.slice(0, -1); // 마지막 글자만 제거
console.log("deleteButton");
}
if (playerTouchingSubmitButton) {
submitButton.setTexture('submit_pressed'); // 눌림 이미지로 변경
setTimeout(() => submitButton.setTexture('submit'), 200); // 200ms 후 원래 이미지로 복구
submitButtonPressed(submitButton);
}
원래 페이저 애니메이션이 이런 식이야?
아니요, Phaser의 애니메이션은 보통 제가 위에서 사용한 방식과는 다르게, Phaser 애니메이션 시스템을 사용합니다. Phaser에는 이미지 텍스처를 단일 프레임으로 전환하는 대신, 프레임 기반 애니메이션을 만드는 시스템이 있습니다. 이 시스템은 스프라이트 시트나 개별 프레임을 빠르게 전환하는 방식으로 애니메이션을 처리합니다.
만약 스프라이트 시트를 사용하여 애니메이션을 만들고 싶다면, Phaser의 create
메서드에서 애니메이션을 등록한 다음 재생할 수 있습니다.
애니메이션에 필요한 스프라이트 시트를 미리 로드합니다.
function preload() {
// 스프라이트 시트 로드 (각 프레임의 크기가 32x32라고 가정)
this.load.spritesheet('button_sprites', '/assets/button_spritesheet.png', {
frameWidth: 32,
frameHeight: 32
});
}
create
메서드에서 애니메이션을 정의하고, 특정 상황에서 재생할 수 있습니다.
function create() {
// 버튼을 추가
const button = this.add.sprite(400, 300, 'button_sprites', 0).setInteractive();
// 애니메이션 생성 (스프라이트 시트의 프레임들을 순서대로 재생)
this.anims.create({
key: 'button_press',
frames: this.anims.generateFrameNumbers('button_sprites', { start: 0, end: 1 }), // 0번 프레임에서 1번 프레임까지
frameRate: 10, // 초당 프레임 수
repeat: 0 // 1회 실행 후 멈춤
});
// 버튼을 클릭했을 때 애니메이션 재생
button.on('pointerdown', () => {
button.play('button_press');
});
}
프레임 기반 애니메이션:
애니메이션 관리가 간편:
this.anims.play
메서드를 사용하여 애니메이션을 손쉽게 재생하고, 재생이 끝나면 자동으로 중단됩니다.에이 애니메이션 써보려면 스프라이트가 필요한거네
너무 시간이 많이 들어갈 것 같아서 진짜 여기까지
https://github.com/lazyArtisan/website1
# 뒤에서 부터 읽은 문자열의 첫번째 문자가 앞읽 문자열에서 발견됐다면
# 그 다음 문자가 뒤읽 문자열의 다음 문자와 같은지 보고, 같지 않다면 탈락
# 앞읽 문자열의 마지막 문자까지 확인했다면 통과.
# 문자열의 길이에서 통과한 문자열 빼면 답.
import sys
input = sys.stdin.readline
S = input().strip()
iS = ""
for i in range(len(S)-1,-1,-1): # 세번째 인자 빼먹어서 헤맴
iS+=S[i]
while 1:
i=0
while i < len(S) and S[i]==iS[i]:
i+=1
if i==len(S):
break
else:
S=S[i+1:]
print(2*len(iS)-len(S))
아무리 생각해도 로직이 틀린게 없는 것 같길래
기분이 더럽지만 질문 게시판을 봄.
예제: aaabbbaa
내 출력: 14
답: 9
# 뒤에서 부터 읽은 문자열의 첫번째 문자가 앞읽 문자열에서 발견됐다면
# 그 다음 문자가 뒤읽 문자열의 다음 문자와 같은지 보고, 같지 않다면 탈락
# 앞읽 문자열의 마지막 문자까지 확인했다면 통과.
# 문자열의 길이에서 통과한 문자열 빼면 답.
import sys
input = sys.stdin.readline
S = input().strip()
iS = ""
for i in range(len(S)-1,-1,-1): # 세번째 인자 빼먹어서 헤맴
iS+=S[i]
while 1:
i=0
while i < len(S) and S[i]==iS[i]:
i+=1
if i==len(S):
break
else:
S=S[1:]
print(2*len(iS)-len(S))
같은 거였다고 건너뛰어버리면 생략되는 경우의 수가 생기는 거였음.
인덱스 하나하나씩
실전에서 이런거 당할수도 있을 거 생각하니까 기분이 너무 더럽다.
내가 실행하는 연산이 정확히 무엇을 하는지,
그러니까 어떻게 무엇을 건너뛰고 건너뛰지 않는지,
그 건너뜀에는 확실한 근거가 있는지 생각을 하고 코드를 적어야 됨...
내가 왜 저렇게 코드를 짰는지 복기해보니,
그냥 i+1부터 문자열을 자른다는 거에 아무런 자각이 없었음.
졸려서 그런 건 알겠는데, 졸렸으면 저 코드를 짜지를 못했어야 함.
내가 이 코드를 왜 짜야 하는지에 대한 근거가 없으면 짜지를 마셈.
어... 풀긴 했는데 이건 좀
# 배열 5000개 세워놓고 기록한 다음에 역순으로 더해나가다가
# 내가 지금 보고 있는 인덱스보다 cnt가 같거나 높다면 그게 H-index
cntL=[0]*5000
for c in citations:
cntL[c]+=1
cnt=0
for i in range(4999,-1,-1):
cnt+=cntL[i]
if cnt >= i:
break
return i
발전하긴 했는데 여전히 쓰레기.
일단 내일 마저 하자.