アンリアルクエスト5で勉強したProcedural Meshでオブジェクトを切るについて

プロジェクトのサンプルはアンリアルクエスト5のプロジェクトを使いました。

https://historia.co.jp/unrealquest05 のDiscordから #プロジェクトデータ配布のチャンネルからDLできます。Discordサーバーが消えた場合はあとで考えます。

使用バージョンは
UE5.2.0

切られるオブジェクトを用意する

ブループリントクラス>Actorで作成します。

名前はBP_ProceduralMesh_Actor としました。

作成したブループリントを開き

DefaultSceneRootの下に

ProceduralMeshとCubeまたはキューブを追加します。

Cubeコンポーネントの設定

M_Procedralというマテリアルを作って

/Game/UnrealQuest5/Props/LevelPrototyping/Meshes/SM_Cubeをコピーしてきて
CubeコンポーネントのスタティックメッシュをSM_Cubeに変更

コピーしてきたSM_Cubeの設定を行います。

AllowCPUAccess にチェックを入れてCPUからでもメッシュ情報にアクセスできるようにします。

Procedural Meshコンポーネントの設定
Procedural Meshコンポーネントの Use Complex as Simple Collisionのチェックを外します。

コンストラクタのノードを設定する
ProceduralMeshに対してCube情報をコピーしてCubeコンポーネントを削除します。

コピーしてきたレベルに作成したBPをおいてみました。

BP_UQ5_Playerを開いて

BladeMeshのOnConponentBeginOverlapをクリックしてノードを作ります。

これ動くのSet Simulate Physics だけだったので、結局

ファーストパーソン を追加

/Game/FirstPerson/Blueprints/BP_FirstPersonProjectile をコピーして

BP_ProceduralBulletとする

On Component Hit をこのように改造する

BP_UQ5_PlayerにBullet_Spawnのカスタムイベントを追加

通常攻撃をコンボにカスタムしてあるけど、こんな感じでSequenceでBullet_Spawnを呼ぶ

BP_ProceduralBulletの当たり判定がデカくなるようにスケールをデカくした

できたのがこちら

アンリアルクエスト5のDiscordでこのページを見た兎月さんがもっとすごい実装方法をしていたので許可を頂いたので、こちらに張らせていただきます
https://uq5.netlify.app/UE5-1-1_Cut_with_a_blade.png

兎月さんに感謝!できたのがこれ。

きもちいですねカットした法線どおりに切れると!

参考サイト

SkeltalMeshをカット?
https://www.unrealengine.com/marketplace/en-US/product/slice-skeletal-mesh-vr?sessionInvalidated=true

[UE4/UE5]MasterPoseComponentの説明

ぷちコンでも乱用した

MasterPoseComponentの説明です。

これがあればぶっちゃけリターゲットとか必要なくない?

みたいなお話です。

大きいEnableMasterPose図はこれです。

アップのEnableMasterPoseはこれです。

UE5.1ではこうしないと

ブループリントランタイム エラー:”プロパティ SkeletalMeshComponent の読み取りを試行するためのアクセスはありません”。 ノード: ブランチ グラフ: EnableMasterPose 関数: Enable Master Pose ブループリント: BP_ThirdMetaHuman

のエラー出た から 最初からSkeletel Mesh Component をIsVaildしたら治った。

添わせたいスケルタルメッシュを以下のように設定するだけで

あたまがこれです。

そうびがこれです。

グレイマンはトランスペアレントなマテリアルで見せてません。

グレイマンはVisibleはTrueにしておかないと壊れます。

こうしているせいで全モーションはグレイマンに追加するだけででOKです。
どんどんグレーマンが鬼カスタムモーション化していくだけなので他のプロジェクトを作る際も鬼グレイマンさえ移行すればいいから簡単です。

以上でした。

5.1のバージョンはこんな感じでした。

Meshの下に入れ子にするのがポイント

参考

https://forums.unrealengine.com/t/topic/402278

ikソルバーとは?

ikつまり逆運動学の問題を解く計算方法のこと
たとえば

つま先から各関節の位置を決める計算方法のことで

ikハンドル(エンドエフェクタ)の位置や姿勢が与えられたときに、その位置や姿勢を達成するために必要な各関節の角度を求めます。

ソルバー計算で、この逆運動学問題を解くことで、3Dキャラクターやロボットアームなどの動きを制御することができます。

IKソルバー計算には、様々な種類があります。代表的なものには、運動学的制約を用いる数値的解法や、数学的最適化問題を解く方法などがあります。

IKソルバーは、3Dキャラクターやロボットアームなどの動きを制御するためのアルゴリズムのことです。
例えば、ロボットアームが目標の位置に移動するために、各関節の角度を計算して制御する際に使用されます。これにより、子供たちも楽しめるロボットの動きが実現されます。

MayaにChatGPT(openai)を追加インストールしてみる

Mayaのpip書き換えちゃうので自己責任でというかMayaの再インスト―ルもする余裕があるときにやること

とりあえずpipでインストールする前にpipのバージョンがふるいのでUpgradeしてる。

cd "C:\Program Files\Autodesk\Maya2023\bin\"
mayapy.exe -m pip install --upgrade pip

mayapy -m pip install openai
cmd /k

入った。

てかこの2フォルダをsitepackageにいれとくだけでも動くには動くはず
https://drive.google.com/file/d/12KGnpQAnkWn3wifXPsMehqZ5YFfeFhWe/view?usp=share_link

メインのPython

import os
import openai


print(os.getcwd())
#print(__file__)
MELpath=os.getcwd()
pythonPath= os.path.abspath(MELpath+"/../python/open_ai/")
pythonPath= pythonPath.replace('\\', '/')
print("pythonPath= "+pythonPath)

f = open(pythonPath+'/open_ai_api_key.txt', 'r', encoding='UTF-8')
open_ai_api_key = f.read()
f.close()

#openai.api_key = os.getenv("OPENAI_API_KEY")
openai.api_key = open_ai_api_key

# APIコールを行う関数
def completion(new_message_text:str, settings_text:str = '', past_messages:list = []):
    if len(past_messages) == 0 and len(settings_text) != 0:
        system = {"role": "system", "content": settings_text}
        past_messages.append(system)

    new_message = {"role": "user", "content": new_message_text}
    past_messages.append(new_message)

    result = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=past_messages
    )
    message_text = result.choices[0].message.content
    response_message = {"role": "assistant", "content": message_text}
    past_messages.append(response_message)
    
    return message_text, past_messages
    
    
    
import re
# 返答をPythonコードとその他の部分に分解
# reモジュールを使って```python~```で囲まれた部分とそれ以外の部分を切り出します。
def decompose_response(txt):
    pattern = r"```python([\s\S]*?)```"
    
    code_list = re.findall(pattern, txt)
    for i in range(int(len(code_list))):
        code_list[i] = re.sub('\A[\r?\n]', '', code_list[i])
        code_list[i] = re.sub('[\r?\n]\Z', '', code_list[i])
    
    comment = re.sub(pattern, '', txt)
    comment = re.sub('[\r?\n]+', '\n', comment)
    comment = re.sub('[\r?\n]\Z', '', comment)
    
    return comment, code_list
    
    
    


from maya import cmds

class ChatGPT_Maya(object):

    def __init__(self):
        self.system_settings = "質問に対して、MayaのPythonスクリプトを書いてください。スクリプト以外の文章は短めにまとめてください。"
        self.message_log = []
        self.at_first = True
        self.create_window()

    def reset_session(self, *args):
        self.message_log = []
        self.at_first = True
        cmds.scrollField(self.input_field, e=True, tx='')
        cmds.scrollField(self.ai_comment, e=True, tx='')
        cmds.cmdScrollFieldExecuter(self.script_field, e=True, t='')

    def call(self, *args):
        user_input = cmds.scrollField(self.input_field, q=True, tx=True)
        
        # APIコール
        if self.at_first:
            message_text, self.message_log = completion(user_input, self.system_settings, [])
            self.at_first = False
        else:
            message_text, self.message_log = completion(user_input, '', self.message_log)
            
        # 返答を分解
        comment, code_list = decompose_response(message_text)
        
        # Pythonコード以外の部分をai_commentに表示
        cmds.scrollField(self.ai_comment, e=True, tx=comment)

        # Pythonコードの1つ目をscript_fieldに表示。2つ目以降は無視(汗
        if code_list:
            cmds.cmdScrollFieldExecuter(self.script_field, e=True, t=code_list[0])
            # 実行
            if cmds.checkBox(self.script_exec, q=True, v=True):
                cmds.cmdScrollFieldExecuter(self.script_field, e=True, executeAll=True)
        else:
            cmds.cmdScrollFieldExecuter(self.script_field, e=True, t='')
    
    # UI作成
    def create_window(self, *args):
        cmds.window(title=u'ChatGPTがPythonスクリプトを書くよ!', width=600, sizeable=True)
        
        cmds.columnLayout(adj=True, cat=['both',5], rs=5)
        self.reset_button = cmds.button(label='Reset', c=self.reset_session) #セッションのリセット
        self.input_field = cmds.scrollField(h=50, ed=True, ww=True, tx='', ec=self.call) #テンキーのEnterで送信
        self.script_exec = cmds.checkBox(label=u'実行もする', align='left', v=True) #Checkが入っていたら返答と同時にスクリプトを実行
        cmds.separator(h=10, st='in')
        self.ai_comment = cmds.scrollField(h=100, ed=False, ww=True, tx='') #コードブロック以外の部分を表示する場所
        cmds.text(l='Script:', align='left')
        self.script_field = cmds.cmdScrollFieldExecuter(st='python', h=200) #Pythonコードを表示・実行する場所
        cmds.setParent('..')

        cmds.showWindow()
        
        
        
        
ChatGPT_Maya_ins=ChatGPT_Maya()

エラーが返ってくる

openai : error_code=None error_message=’You exceeded your current quota, please check your plan and billing details.’ error_param=None error_type=insufficient_quota message=’OpenAI API error received’ stream_error=False

エラー: RateLimitError: file C:\Program Files\Autodesk\Maya2023\Python\lib\site-packages\openai\api_requestor.py line 679: You exceeded your current quota, please check your plan and billing details.

このエラーは

https://platform.openai.com/account/billing/overview

で Payment methods と Billing preferencesPrimary business addressを設定したら動くようになった。

お世話になった記事

https://qiita.com/akasaki1211/items/34d0f89e0ae2c6efaf48

https://okumuralab.org/~okumura/python/openai_api.html

https://knowledge.autodesk.com/ja/support/maya/learn-explore/caas/CloudHelp/cloudhelp/2023/JPN/Maya-Scripting/files/GUID-72A245EC-CDB4-46AB-BEE0-4BBBF9791627-htm.html

https://qiita.com/sakasegawa/items/db2cff79bd14faf2c8e0

https://tomo-web.jp/chat-gpt-you-exceeded-your-current-quota/#index_id0

[Unreal Engine]複数のカメラのスクリーンショットを自動的に撮るPython

複数のカメラのスクリーンショットを自動的に撮るPython

Log Python: Automation Library: Failed to convert parameter ‘camera’ when calling function ‘AutomationBlueprintFunctionLibrary.TakeHighResScreenshot’ on ‘Default__AutomationBlueprintFunctionLibrary’ TypeError: NativizeProperty: Cannot nativize ‘SceneCaptureActor’ as ‘Camera’ (ObjectProperty) TypeError: NativizeObject: Cannot nativize ‘SceneCaptureActor’ as ‘Object’ (allowed Class type: ‘CameraActor’)

IS FIX Code

import unreal
import time

class OnTick(object):
  """ Register a function and run it on tick, """
  def __init__(self):
    # make a generator function so we can call next on it,
    
    EditorLevelLibrary = unreal.EditorLevelLibrary()
    all_level_actors=EditorLevelLibrary.get_all_level_actors()
    CameraActors=[]
    for actor in all_level_actors:
        #CameraBool=actor.find_camera_component_when_view_target
        CameraComponent=actor.get_component_by_class(unreal.CameraComponent)
        #print("CameraComponent="+str(CameraComponent))
        #className=CameraComponent.get_class() 
        #print("CameraComponent.get_class() ="+str(className))
        if(str(CameraComponent)=="None"):
            pass
        else:
            #if(str(CameraComponent) in "CineCameraComponent"):
            #    pass
            #else: 
            CameraActors.append(actor)
    
    self.CameraActors=CameraActors
    #self.actors = (actor for actor in unreal.EditorLevelLibrary.get_selected_level_actors())
    self.actors = iter(CameraActors)
    
    self.tickcount=0
    self.CapturedCount=0
    # register a callback on tick
    self.on_tick = unreal.register_slate_post_tick_callback(self.__tick__)
    
    
  def __tick__(self, deltatime):
    """ Function we will call every tick, """
    try:
      print(deltatime) # Print the deltatime just for sanity check so we
      # know a tick was made,
        
      # Get the next actor from our generator function,
      actor = next(self.actors)
      """
      actor_location = actor.get_actor_location()
      actor_rotation = actor.get_actor_rotation() 
      # Solve the camera position, and rotation
      position = actor_location + unreal.Vector(0.0, 0.0, 0.0)

      # roll(z), pitch(tate), yaw(yoko)
      rotation = unreal.Rotator(actor_rotation.roll+0.0, actor_rotation.pitch+5.0,actor_rotation.yaw+0.0)
      
      unreal.EditorLevelLibrary.set_level_viewport_camera_info(
        position,
        rotation
      )
      """
      errorBool=False
      try:
          CameraComponent=actor.get_component_by_class(unreal.CameraComponent)
          print("actor= "+str(actor))
          print("CameraComponent= "+str(CameraComponent))
          unreal.AutomationLibrary.take_high_res_screenshot(
            1920,
            1080,
            str(self.CapturedCount)+"_"+actor.get_name() + ".hdr",
            camera=actor,
            capture_hdr=True,
            comparison_tolerance=unreal.ComparisonTolerance.HIGH
          )
          errorBool=False
          
      except Exception as error:
          errorBool=True
          pass
      
      if(errorBool==False):
          # Count
          self.CapturedCount=self.CapturedCount+1
      """
      unreal.EditorLevelLibrary.pilot_level_actor(actor)
      unreal.EditorLevelLibrary.editor_set_game_view(True)
      # Take the screenshot,
      unreal.AutomationLibrary.take_high_res_screenshot(
        1920,
        1080,
        actor.get_name() + ".hdr",
        camera=actor,
        capture_hdr=True,
        comparison_tolerance=unreal.ComparisonTolerance.HIGH,
        delay=0.25
      )
      
      unreal.EditorLevelLibrary.eject_pilot_level_actor()
      """
      # Count
      self.tickcount=self.tickcount+1
      
      CameraActors=self.CameraActors
      CameraActorsLen=len(CameraActors)
      endActor=CameraActors[CameraActorsLen-1]
      print("self.tickcount="+str(self.tickcount)+"  :CameraActorsLen="+str(CameraActorsLen))
      if(str(self.tickcount)==str(CameraActorsLen)):
          print("finish")
          unreal.unregister_slate_post_tick_callback(self.on_tick)
      
    except Exception as error:
      #pass
      print(error)
      try:
          unreal.unregister_slate_post_tick_callback(self.on_tick)
      except Exception as error:
          pass
#if __name__ == '__main__':
instance = OnTick()

関連URL

https://forums.unrealengine.com/t/python-api-highrescreenshot/132783/10

https://docs.unrealengine.com/5.1/en-US/PythonAPI/class/EditorLevelLibrary.html

https://docs.unrealengine.com/5.0/en-US/PythonAPI/class/AutomationLibrary.html

https://docs.unrealengine.com/5.1/en-US/taking-screenshots-in-unreal-engine/

https://docs.unrealengine.com/4.26/en-US/BlueprintAPI/Utilities/Struct/MakeAutomationScreenshotOptions/

pythonの標準モジュールで windowsにクリップボードにコピーするには?

pyperclipを使わないで、pythonの標準モジュールで windowsにクリップボードにコピーするには?

import subprocess 
s="abcdefg"
print("s=__"+s+"__")
subprocess.run("clip", text=True, input=s)

参考

Python script to copy text to clipboard [duplicate]

https://stackoverflow.com/questions/11063458/python-script-to-copy-text-to-clipboard

[clip / pbcopy・pbpaste]クリップボードにコピー

https://xtech.nikkei.com/it/atcl/column/15/042000103/080400036/

[UnrealEngine]C++ シンプルなクラス構文

クラスの定義はヘッダファイルで行います。

#include <string>
using namespace std;
class Class1
{
// 
private:
	string name; //メンバ変数
// 
public:
	void print1(); //メンバ関数のプロトタイプ宣言
	void print2(); //メンバ関数のプロトタイプ宣言
// 
};

cppでのメソッドの実装

void クラス::実装するメソッド名()

#include "Class1.h"
#include <iostream>
using namespace std;

// 
void Class1::print1()
{
	name = "print1";
	cout << name << "が処理されました\n";

	return;
}
// 
void Class1::print2()
{
	name = "print2";
	cout << name << "が処理されました。どうしてニンニク味のガムってないんだろう?\n";

	return;
}

クラスからインスタンスを生成して利用するサンプルです。

Class1 *インスタンス名 = new Class1();

インスタンス名 – > 使用するメソッド名();

#include "Class1.h"

int main() {

	Class1 *cla2 = new Class1();
	cla2->print1(); //print1が処理されました
	cla2->print2(); //print2が処理されました
	delete cla2;

	return 0;
}

[unrealenghine,photoshop]HDRは32bitなので16bitに変換しないとPNGに保存できない、8bitに変換しないと TGA に保存できない

Can Not HDR Traslate To TGA even if CopySave

 function step_8bit(enabled, withDialog) {
    if (enabled != undefined && !enabled)
      return;
    var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
    var desc1 = new ActionDescriptor();
    desc1.putInteger(cTID('Dpth'), 8);
    var desc2 = new ActionDescriptor();
    desc2.putInteger(cTID('Vrsn'), 6);
    desc2.putEnumerated(cTID('Mthd'), sTID("hdrToningMethodType"), sTID("hdrtype4"));
    desc2.putDouble(cTID('Rds '), 7.68);
    desc2.putDouble(cTID('Thsh'), 0.52959457138863);
    desc2.putDouble(cTID('Cntr'), 1);
    desc2.putDouble(cTID('Strt'), 20);
    desc2.putDouble(cTID('Dtl '), 30);
    desc2.putBoolean(cTID('Smoo'), false);
    var desc3 = new ActionDescriptor();
    desc3.putString(cTID('Nm  '), "初期設定");
    var list1 = new ActionList();
    var desc4 = new ActionDescriptor();
    desc4.putDouble(cTID('Hrzn'), 0);
    desc4.putDouble(cTID('Vrtc'), 0);
    list1.putObject(cTID('CrPt'), desc4);
    var desc5 = new ActionDescriptor();
    desc5.putDouble(cTID('Hrzn'), 255);
    desc5.putDouble(cTID('Vrtc'), 255);
    list1.putObject(cTID('CrPt'), desc5);
    desc3.putList(cTID('Crv '), list1);
    desc2.putObject(sTID("classFXShapeCurve"), cTID('ShpC'), desc3);
    desc2.putBoolean(sTID("deghosting"), false);
    desc1.putObject(cTID('With'), sTID("hdrOptions"), desc2);
    executeAction(sTID('convertMode'), desc1, dialogMode);
  };  

  //save TGA  (Can Not  HDR Traslate To TGA even if CopySave)
  function step4_save_tga(savePath){
  step_8bit();
  SaveOptions = new TargaSaveOptions();     
  TargaSaveOptions.alphaChannels = true; //include alpha channels, change to false for none    
  TargaSaveOptions.resolution = TargaBitsPerPixels.TWENTYFOUR; //options of SIXTEEN or THIRTYTWO 
  TargaSaveOptions.rleCompression = true; //false for no compression
  var saveFile = File(savePath); 
  app.activeDocument.saveAs(saveFile, TargaSaveOptions, true,Extension.LOWERCASE);      
  }; 

//---------------------------------------------------------------------------------
function step1_16bit(enabled, withDialog) {
    if (enabled != undefined && !enabled)
      return;
    var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
    var desc1 = new ActionDescriptor();
    desc1.putInteger(cTID('Dpth'), 16);
    var desc2 = new ActionDescriptor();
    desc2.putInteger(cTID('Vrsn'), 6);
    desc2.putEnumerated(cTID('Mthd'), sTID("hdrToningMethodType"), sTID("hdrtype4"));
    desc2.putDouble(cTID('Rds '), 7.68);
    desc2.putDouble(cTID('Thsh'), 0.52959457138863);
    desc2.putDouble(cTID('Cntr'), 1);
    desc2.putDouble(cTID('Strt'), 20);
    desc2.putDouble(cTID('Dtl '), 30);
    desc2.putBoolean(cTID('Smoo'), false);
    var desc3 = new ActionDescriptor();
    desc3.putString(cTID('Nm  '), "初期設定");
    var list1 = new ActionList();
    var desc4 = new ActionDescriptor();
    desc4.putDouble(cTID('Hrzn'), 0);
    desc4.putDouble(cTID('Vrtc'), 0);
    list1.putObject(cTID('CrPt'), desc4);
    var desc5 = new ActionDescriptor();
    desc5.putDouble(cTID('Hrzn'), 255);
    desc5.putDouble(cTID('Vrtc'), 255);
    list1.putObject(cTID('CrPt'), desc5);
    desc3.putList(cTID('Crv '), list1);
    desc2.putObject(sTID("classFXShapeCurve"), cTID('ShpC'), desc3);
    desc2.putBoolean(sTID("deghosting"), false);
    desc1.putObject(cTID('With'), sTID("hdrOptions"), desc2);
    executeAction(sTID('convertMode'), desc1, dialogMode);
  };

  //save Png 
  function step4_save(savePath,enabled, withDialog) {
  step1_16bit();
  var file_obj = new File(savePath);
  var png_opt = new PNGSaveOptions();
  png_opt.interlaced = false;
  var savedDisplayDialogs = app.displayDialogs;
  app.displayDialogs = DialogModes.NO;
  activeDoc=app.activeDocument;
  app.activeDocument.saveAs(file_obj, png_opt, true, Extension.LOWERCASE);
  
  }
//---------------------------------------------------------------------------------


  // save HDR (HDR to HDR)
  function step4_save(savePath,enabled, withDialog) {
    if (enabled != undefined && !enabled)
      return;
    var dialogMode = (withDialog ? DialogModes.ALL : DialogModes.NO);
    var desc1 = new ActionDescriptor();
    desc1.putString(cTID('As  '), "Radiance");
    desc1.putPath(cTID('In  '), new File(savePath));
    desc1.putInteger(cTID('DocI'), 70);
    desc1.putBoolean(cTID('LwCs'), true);
    executeAction(sTID('save'), desc1, dialogMode);
  };

参考

Targa形式で保存する

http://www.openspc2.org/book/PhotoshopCC2014/easy/save/010/index.html