Blueprint – Jake Tee https://jakesto.com Game Dev Blog Sun, 10 Dec 2023 03:33:58 +0000 en-US hourly 1 https://wordpress.org/?v=6.8.1 https://jakesto.com/wp-content/uploads/2022/07/cropped-pngwing.com_-32x32.png Blueprint – Jake Tee https://jakesto.com 32 32 194548219 Rich Text Block – Thay đổi cấu trúc chữ như lập trình web https://jakesto.com/rich-text-block-thay-doi-cau-truc-chu-nhu-lap-trinh-web/?utm_source=rss&utm_medium=rss&utm_campaign=rich-text-block-thay-doi-cau-truc-chu-nhu-lap-trinh-web Sun, 10 Dec 2023 03:33:19 +0000 https://jakesto.com/?p=1005 Nếu anh em nào từng code web thì sẽ quá quen với cấu trúc để thay đổi kiểu chữ <p></p> hay là <title></title>

Tuy nhiên mặc định trong Unreal Engine lại không có sẵn vậy. Giả sử ta có một đoạn chữ sau:

“Tôi yêu lập trình game”

Với web, sẽ rất dễ để ta thay đổi giả sử chữ game thành in đậm, với cú pháp đơn giản là:
<title>Tôi yêu lập trình <p>game</p></title>

Còn trong Unreal, ta sẽ phải làm như thế này:

May mắn là do chữ game ở cuối, nên ta chỉ cần 2 TextBlock, nếu không sẽ cần nhiều hơn thế chỉ cho một đoạn chữ đơn giản.

Càng nhiều text block, càng tốn tài nguyên xử lí thêm. Để giải quyết vấn đề này, Unreal đã cung cấp một giải pháp tương tự như code web, đó là Rich Text Block.

Cách sử dụng

Tạo DataTable với cấu trúc dữ liệu là RichTextStyleRow

Mình sẽ đặt tên là TextStyles.

Khi mở lên ta sẽ thấy một bảng trống, ấn Add để thêm dữ liệu mới vào.

Ở cột Row Name, đó sẽ là tên cú pháp của chúng ta. Trong ví dụ này mình thêm p để sử dụng cho in đậm chữ.

Ở phần tùy chỉnh bên dưới sẽ, ta cũng sẽ chọn Font Family thành Roboto, và Typeface là Bold.

Cuối cùng, quay lại ví dụ trên, ta sẽ bỏ đi 2 TextBlock cũ, và thay vào bằng 1 RichTextBlock

Ở phần cài đặt, chọn TextStyles table mà ta đã tạo ở bước trước.
Lúc này chữ sẽ lỗi do không tìm thấy style mặc định, ta có thể tick vào Override Default Style và set style mặc định cho nó:

Cuối cùng, ta sẽ format dòng chữ “Tôi yêu lập trình game” -> Tôi yêu lập trình <p>game</> và kết quả:

Lưu ý: Rich Text Block có vẻ vẫn chưa hỗ trợ cấu trúc tròng theo kiểu <h1>hello<p>world</></> nên anh em lưu ý.

]]>
1005
101 Tips – Stack Trace https://jakesto.com/101-tips-stack-trace/?utm_source=rss&utm_medium=rss&utm_campaign=101-tips-stack-trace Sun, 19 Mar 2023 14:57:30 +0000 https://jakesto.com/?p=980 Khi debug lỗi, hẳn các bạn sẽ không lạ việc tìm kiếm xem hàm đó hoặc sự kiện đó được gọi ở đâu. Trong c++, cách thường làm đó là thêm break point vào dòng code cần xem. Thế còn trong Blueprint thì làm như thế nào?
Câu trả lời là Stack Trace.

Giả sử ta có 1 hàm tên là PrintHelloWorld, chỉ đơn giản là Print String ra màn hình dòng “Hello World” như sau:

Để biết được hàm PrintHelloWorld này được gọi từ object nào, chỉ việc thêm Stack Trace vào nối hàm:

Sau khi hàm PrintHelloWorld được gọi, ta sẽ biết được nguồn gốc của việc gọi đó:

LogBlueprintUserMessages: Script call stack:
Function /Game/Blueprints/BP_MyCharacter.BP_MyCharacter_C:InpActEvt_T_K2Node_InputKeyEvent_0
Function /Game/Blueprints/BP_MyCharacter.BP_MyCharacter_C:ExecuteUbergraph_BP_MyCharacter
Function /Game/Blueprints/BP_MyCharacter.BP_MyCharacter_C:PrintHelloWorld
Function /Game/Blueprints/BP_MyCharacter.BP_MyCharacter_C:ExecuteUbergraph_BP_MyCharacter
LogBlueprintUserMessages: [BP_MyCharacter_C_0] Hello World!

Như ở ví dụ trên, PrintHelloWorld được gọi trong BP_MyCharacter, ở sự kiện ấn nút T.

]]>
980
Cơ bản networking https://jakesto.com/co-ban-networking-phan-1/?utm_source=rss&utm_medium=rss&utm_campaign=co-ban-networking-phan-1 Sun, 30 Oct 2022 12:59:55 +0000 https://jakesto.com/?p=968 Phần 1

Trong phần này mình sẽ đi qua 2 khái niệm cơ bản đó là mô hình mạngquyền mạng.

Để tóm gọn, thì mô hình mạng phổ biến đó là Client-Server, với Server sẽ là trung tâm chính quản lí và lưu trữ dữ liệu gốc, sau đó gửi thông tin tới các người dùng. Mọi hoạt động thay đổi các giá trị online đều phải được thông qua bởi Server.

Với role, thì các Actor trong thế giới, luôn có 2 role đó là Local RoleRemote Role. Trong đó, từ phía các Actor, Remote Role sẽ luôn là Role_Authority bởi các Actor có trạng thái online luôn được sở hữu bởi Server. Trong khi Local Role sẽ phụ thuộc vào đối tượng.

Nếu Actor được điều khiển bởi chính người chơi, Local Role sẽ là Role_AutonomousProxy, và với các đối tượng khác sẽ là Role_SimulatedProxy.

Phần 2

Trong phần 2, mình đi qua khái niệm của Remote Procedural Call. Bao gồm các loại:

  • Server Execute: rpc gọi từ dưới client lên server để yêu cầu thực thi một sự kiện hay hành động
  • Client Execute: rpc gọi từ server về phía đối tượng client cụ thể để thực thi một sự kiện hay hành động
  • NetMulticast: rpc gọi từ server tới tất cả đối tượng client trong vùng không gian cho phép để thực thi một sự kiện hay hành động

Phần 3

Trong phần cuối, mình đi qua:

  • OnRep: thuộc tính bắt buộc các sự kiện đi kèm của một giá trị khi có thay đổi.
  • So sánh OnRep và Multicast: OnRep sẽ luôn được gọi khi client trong vùng không gian hỗ trợ hoặc khi vừa có kết nối tới Server. Multicast chỉ gọi một lần tại thời điểm cụ thể và sẽ bị quên đi.
  • Reliable và Unreliable: Reliable là thuộc tính của RPC cho phép Engine ưu tiên để gửi đi, và Unreliable là ngược lại.
]]>
968
101 Tips – Mở level kèm tham số https://jakesto.com/101-tips-mo-level-kem-tham-so/?utm_source=rss&utm_medium=rss&utm_campaign=101-tips-mo-level-kem-tham-so Sun, 18 Sep 2022 12:55:28 +0000 https://jakesto.com/?p=945 Có nhiều mục đích khi mở level kèm tham số. Ví dụ bạn cần kiểm tra xem level trước đó của người chơi là gì, hoặc chỉ đơn giản là muốn truyền một tham số để khi vừa qua level mới có thể sử dụng ngay.
Có nhiều cách để có thể truyền tham số khi mở một level mới, ví dụ như lưu vào GameInstance, rồi sau khi level đã load xong, ta lấy ra sử dụng. Nhưng thứ thế ta lại phải tạo một hay nhiều biến mới không cần thiết.

Một cách đơn giản đó là ta truyền tham số kèm với khi mở một level mới bằng hàm OpenLevel phổ biến

Nếu bạn không thấy mục Absolute và Options ở dưới, chỉ việc click vào mũi tên để xổ ra.

Vùng Options cho phép ta truyền vào một hay nhiều tham số, được ngăn cách nhau bởi dấu “?”:

?PreviousLevel=MyLevel1?MyScore=20

Ở ví dụ trên, ta sẽ truyền qua Level mới giá trị PreviousLevel (level trước đó) là MyLevel1, MyScore là 20.

Tiếp đến, để sử dụng những tham số vừa truyền, ta vào class GameMode. Nếu bạn chưa biết GameMode là gì, có thể tham khảo lại bài viết này: https://jakesto.com/index.php/2020/03/01/unreal-engine-4-framework-gamemode/

Chuột phải vào Event Graph, và tìm cụm từ Options String, đó chính là đoạn Options mà bạn truyền vào ở trên. Tiếp đó ta chỉ cần Parse đoạn string đó với key tương ứng:

Như vậy đã xong, với cách này, bạn đã có thể thoải mái đem giá trị từ level này sang level khác mà không cần lo việc tạo quá nhiều biến.

]]>
945
Unreal Engine 5 – Motion Warping https://jakesto.com/unreal-engine-5-motion-warping/?utm_source=rss&utm_medium=rss&utm_campaign=unreal-engine-5-motion-warping Sat, 02 Jul 2022 08:58:28 +0000 https://jakesto.com/?p=903 Unreal Engine 5 đã cung cấp rất nhiều plugins cũng như tính năng mới tới người dùng. Một tính năng mình thấy rất thích đó là Motion Warping.

Nói tóm tắt:

  • Motion Warping thay đổi Root Motion giữa các frame được định trước từ Montage.
  • Motion Warping thay đổi được transform của animation tại runtime bằng logic từ blueprint hay c++ mà không phải sửa animation.

Trong project mẫu Ancient Valley của Epic Games, nó được sử dụng trong đoạn nhảy qua tảng đá:

Deverse World Attack Motion Warping

Có rất nhiều ứng dụng của Motion Warping, ví dụ như khi player thực hiện combat, cần animation đấm/đá/tấn công quay vào hướng đối tượng.

Trong ví dụ sau, mình sẽ ứng dụng vào sự kiện tấn công tầm gần, sử dụng ví dụ của Paragon : Crunch.

Trước tiên, ta hãy bật plugin Motion Warping từ Plugins.

Trước tiên ta có thể thêm 1 số mục tiêu cần tấn công vào. Mình sẽ sử dụng class nhân vật cơ bản của Unreal, bạn có thể sử dụng gì cũng được.

Mục tiêu của mình là làm cho Crunch tấn công theo các mục tiêu đặt sẵn cũng như là quay hướng đánh vào đúng đối tượng.

Trước tiên ta phải update animation để sử dụng Motion Warping.

Mở animation đánh lên, và tick vào Enable Root Motion:

LƯU Ý: Motion Warping chỉ dùng được khi animation có Root Motion nhé, tức là xương root có thay đổi về vị trí trong animation data.

Bởi vì animation paragon không có Root Motion, nên mình phải sửa tay animation xương root cho di chuyển lên trước. Bạn có thể dễ dàng làm việc này bằng các phần mềm 3D hoặc là dùng additive trong UE rồi xuất ra lại file animation.

Sau khi xong, ta mở montage tấn công lên, trong trường hợp này là mình dùng file Ability_Combo_03_Montage đã có sẵn.

Thay đổi vùng warp theo ý bạn muốn, thông thường sẽ là khi bắt đầu tấn công và kết thúc khi đã ra đòn.

Như vậy là Crunch sẽ warp vị trí và góc quay trong đoạn thời gian vạch ở vùng MotionWarping.

Bước cuối cùng là setup thuộc tính, click vào MotionWarping, bên vùng Details, setup như sau:

  • Root Motion Modifier: loại warp, phổ biến là Skew Warp
  • Warp Target Name: tên sự kiện Warp, bạn có thể set thành gì cũng được. Tên sẽ được sử dụng để cập nhật thông tin cho montage từ logic game.
  • Warp Translation: có muốn warp vị trí không, mặc định là có.
  • Ignore ZAxis: bỏ qua warp theo trục thẳng (trên/dưới).
  • Warp Rotation: có muốn warp góc quay không, mặc định là có.

Như vậy là đã hoàn thành khâu setup cho montage. Ta chuyển qua setup logic cho BP.

Bộ nhân vật Paragon : Crunch đã cung cấp sẵn cho ta blueprint nhân vật CrunchPlayerCharacter. Mở nó lên và thêm Motion Warping component vào vùng Components.

Từ component MotionWarping, kéo thả vào graph và tìm kiếm hàm Add or Update Warp Target from Location and Rotation:

Ở logic trên, mình chỉ đơn giản là lấy blueprint nhân vật mẫu BP_ThirdPersonCharacter của UE trong level, lấy vị trí của nhân vật nối vô Target Location, sau đó tính toán góc cần quay của đối tượng với Crunch. Mình chỉ dùng Target Rotation Z là tại vì mình không muốn Crunch quay lên trời cũng như nghiêng người.

Bước cuối cùng chỉ là play animation montage đã setup ở trên.

LƯU Ý: lí do mình ngắt 2 node Play Anim Montage ở trên là vì animation của chúng không có root motion. Nếu bạn có thì cứ nối bình thường nhé.

Thành quả:

]]>
903
101 Tips – Lightning channel https://jakesto.com/101-tips-lightning-channel/?utm_source=rss&utm_medium=rss&utm_campaign=101-tips-lightning-channel Sat, 28 May 2022 13:54:06 +0000 https://jakesto.com/?p=895 Có bao giờ bạn muốn đèn chỉ cast ánh sáng lên một số vật thể nhất định trong scene? Hay là chỉ đơn giản là muốn hạn chế ánh sáng cast lên quá nhiều vật thể để tối ưu hiệu năng? Trong Unreal Engine có một tính năng khá hay đó là Lighting Channel.

UE cung cấp cho người dùng 3 channel để sử dụng, và mặc định luôn là Channel 0.

Để thay đổi Lightning Channel, tìm kiếm giá trị Lightning Channel trong vùng Details của ánh sáng, bỏ check Channel 0 và đánh dấu vào Channel 1:

Làm tương tự với các vật thể mà bạn muốn ánh sáng được cast lên. Kết quả ta sẽ có như sau:

Như vậy toàn bộ scene đã không còn được nhận ánh sáng từ DirectionalLight, trừ khối hộp bên trái đã chuyển Lightning Channel sang 1.

]]>
895
101 Tips – Format text https://jakesto.com/101-tips-format-text/?utm_source=rss&utm_medium=rss&utm_campaign=101-tips-format-text Tue, 29 Mar 2022 14:00:14 +0000 https://jakesto.com/?p=875 Đây là một tip khá hay để có thể format đoạn chữ. Cách phổ biến mọi người hay dùng thường là sử dụng Append String:

Cách này có một hạn chế lớn đó là khi cấu trúc câu trở nên phức tạp thì việc thêm pin vô sẽ rất rườm rà. Thay vào đó ta có thể sử dụng Format Text:

Với cách này, ta có thể thêm pin vào bằng cách sử dụng cú pháp {char} với char có thể là bất cứ kí tự chữ số nào miễn là không trùng nhau.

Ngoài ra, sử dụng Format Text, các đầu nối pin sẽ không phải convert sang dạng String như khi dùng Append String, ta chỉ việc đổi Text thành String sau cùng.

]]>
875
101 Tips – Macro https://jakesto.com/101-tips-macro/?utm_source=rss&utm_medium=rss&utm_campaign=101-tips-macro Wed, 23 Mar 2022 12:59:45 +0000 https://jakesto.com/?p=864 Nếu các bạn đã từng thấy một node Blueprint như:

kèm với biểu tượng bánh răng ở góc thì đó chính là một Macro. Công dụng của node trên đó là kiểm tra quyền network của dòng logic đi qua.

Vậy Macro là gì, Macro có thể hiểu đơn giản là một node cho phép người dùng kiểm soát dòng di chuyển logic một cách gọn hơn. Nếu như Function hay Custom Event chỉ có một chiều di chuyển thì Macro có thể có một hoặc nhiều chiều.

Nếu chung ta đúp chuột vào node Switch Has Authority, nó sẽ trông như sau:

Về lí thuyết ta có thể kéo giá trị trả lại từ Has Authority và nối vào Branch để kiểm tra quyền network nhưng nếu phải lặp lại nhiều lần như vậy thì đống dây nối của ta sẽ bị dư thừa nhiều hơn.

Để tạo một Macro, click dấu + trong vùng Macro ở MyBlueprint

Trong ví dụ này mình sẽ làm một Macro kiểm tra xem đầu vào có phải số chẵn hay không.

Trong vùng InputsDetails, lần lượt thêm tham số đầu vào FloatExec. Định dạng Exec chính là một dạng đặc biệt cho phép dòng logic đi qua (hay còn thấy ở mỗi đầu node Blueprint).

Trong vùng Outputs, lần lượt thêm tham số đầu ra Exec cho số chẵn và số lẻ.

Logic thì chỉ đơn giản là kiểm tra số dư khi chia 2, nếu dư thì ta cho dòng logic đi vào nối Even Number và lẻ thì vào Odd Number.

Sau đó chỉ việc kéo ra sử dụng và kết quả khi truyền số 11 vào:

]]>
864
101 Tips – Event collision khi overlap particle https://jakesto.com/101-tips-event-collision-khi-overlap-particle/?utm_source=rss&utm_medium=rss&utm_campaign=101-tips-event-collision-khi-overlap-particle Sun, 13 Feb 2022 03:43:37 +0000 https://jakesto.com/?p=852 Nếu mọi người đã quen với Event Begin Overlap hay Event Component Begin Overlap thì Event Particle Collide của particle cũng tương tự như thế, nó sẽ gọi khi 1 actor overlap vào particle.

Một ví dụ cho việc sử dụng On Particle Collide đó là lửa của molotov trong CS:GO

Trong ví dụ lần này mình sẽ sử dụng particle lửa trong Starter Content của Unreal Engine.

Để thuận tiện cho sử dụng, ta nên duplicate P_Fire thành P_FireCollision.

Mở P_FireCollision lên, và thêm module Actor Collision vào cái emitter.

  • Damping factor: particle sẽ nhả emitter chậm lại bao nhiêu khi bị collide. Hiện tại mình để giá trị cố định là 1 cho đơn giản.
  • Max Collisions: số lượng collision tối đa, mình để là 1 để tránh trigger quá nhiều event
  • Collision Types: ObjectType là particle sẽ tương tác, mình set thành Pawn để tương tác với nhân vật điều khiển.

Ngoài ra để cho test, mình cũng tắt Collide Only if Visible đi và tăng Max Collision Distance lên 100m.

Như vậy đã xong phần setup của Particle, kế đến ta cần một actor để xử lí event overlap. Đơn giản tạo một actor mới và đặt tên là BP_FireActor. Actor sẽ có một component đó là ParticleSystem, được set Template thành P_FireCollision ta tạo trên.

Ở mục Events của ParticleSystem, click chọn On Particle Collide.

Mình setup cho nó sẽ in ra màn hình Burning để test nhé. Sau đó compile và thả actor vào level.

Kết quả:

Lưu ý: particle sẽ trigger collide liên tục do emitter được chạy liên tục, để tránh xử lí event quá nhiều các bạn có thể thêm delay hoặc timer nhé.

]]>
852
101 Tips – Delay cho AI Task https://jakesto.com/101-ai-delay-task/?utm_source=rss&utm_medium=rss&utm_campaign=101-ai-delay-task Sun, 05 Dec 2021 01:11:01 +0000 https://jakesto.com/?p=843 Mọi người có lẽ đều quen biết node Delay để dừng flow logic trong blueprint, tuy nhiên khi sử dụng trong BlueprintTask cho hành vi AI thì dường như nó không hoạt động. Thể làm thế nào để delay một hay nhiều chuỗi logic trong Task ?

Cùng coi ví dụ một Task setup dưới đây. Trước hết cần 2 event sau:
Event Execute AI

Event Tick AI

Nếu bạn không biết tìm Event trên ở đâu thì các bạn tìm đến Override ở vùng Functions

Sau đó tiếp tục tạo 2 biến float lần lượt:

  • CurrentDelay: sẽ lưu thời gian delay hiện tại, thay đổi theo tick
  • Delay: thời gian delay mặc định tối đa. Khi sửa ta sẽ sửa giá trị này

Đối với Delay, tick chọn Instance Editable để có thể sửa trực tiếp trên Task khi đặt ở BehaviorTree.

Khi Event Receive Execute AI được gọi, ta set giá trị ban đầu cho biến CurrentDelay:

Kế đến, ở Event Receive Tick AI, ta chỉ việc giảm giá trị CurrentDelay theo từng tick cho tới khi giá trị bé hơn hoặc bằng 0:

Như vậy là đã xong Delay cho Task. Khi Task được gọi xong nhớ lưu ý Finish Execute Success để Task không kẹt nhé.

]]>
843