基本的に、私は 2D スケルトン アニメーション エディターと、エディターで作成された同じアニメーションをゲームで簡単に使用できるようにするフレームワークを作成しようとしています。最大の問題は、私が以前に XML パーサーを作成したことがないことです。そのため、何か完全に間違ったことをしているような気がしますが、それが何であるかはわかりません。
私の形式のファイルの例を次に示します。
<skeleton name="skeleton1">
<bones>
<bone name="bone1" id="1" textureLoc="/imgs/test.png">
<position>x,y</position>
<rotation>float</rotation>
<scale>float</scale>
</bone>
<bone name="bone2" id="2" textureLoc="/imgs/text2.png">
<position>x,y</position>
<rotation>float</rotation>
<scale>float</scale>
</bone>
</bones>
<animation name="anim1">
<keyframe frameToPlay="1">
<bone id="1" transPosition="x,y" transRotation="float" transScale="float">
</bone>
<bone id="2" transPosition="x,y" transRotation="float" transScale="float">
</bone>
</keyframe>
<keyframe frameToPlay="50">
<bone id="1" transPosition="x,y" transRotation="float" transScale="float">
</bone>
<bone id="2" transPosition="x,y" transRotation="float" transScale="float">
</bone>
</keyframe>
</animation>
<animation name="anim2">
<keyframe frameToPlay="1">
<bone id="1" transPosition="x,y" transRotation="float" transScale="float">
</bone>
<bone id="2" transPosition="x,y" transRotation="float" transScale="float">
</bone>
</keyframe>
<keyframe frameToPlay="50">
<bone id="1" transPosition="x,y" transRotation="float" transScale="float">
</bone>
<bone id="2" transPosition="x,y" transRotation="float" transScale="float">
</bone>
</keyframe>
</animation>
</skeleton>
コードをより理解しやすくするために、このシステムをどのように編成したかを以下に示します。
AnimationSystem – Management interface for all animations in game, handles updating of each skeleton
--List of skeletons
---Skeletons represent each object
--Update method that updates each skeleton with the game’s current frame
Animation
--List of keyframes
--List of frames, in ascending order, pulled from the list of keyframes
---Order index in list corresponds to order index in list of keyframes
---Should help to speed things up a little bit, no need to search every keyframe to determine which one has a frame value matching the one that is currently needed.
---Will be determined by a set up method after assets are loaded, but before the animation can be used.
--Method to return a keyframe that corresponds to a frame passed to the method
Skeleton
--List of animations
--List of bones
--Value to hold the currently selected animation
--Value to hold the current frame in the game
--Value to hold the name of the skeleton
--Upon moving to a new frame, searches currently selected animation’s list of frames to see if a keyframe should be “played” this frame. If a keyframe exists that should be played at this frame, it should go through each bone in its list of bones, and set the properties of each bone to the properties listed in the keyframe according to the bone’s id.
Bone
--Value to hold the stationary position
--Value to hold the stationary rotation
--Value to hold the stationary scale
--Value to hold the transformed position
--Value to hold the transformed rotation
--Value to hold the transformed scale
--Image value that holds the texture of the bone
--Value to hold the id of the bone within its skeleton
--Value to hold the name of the bone (Used in the editor)
--Method that takes 3 values (position, rotation and scale in that order) and set’s its own transformed pos, rot and scale values to those passed in.
Keyframe
--List of scale values
--List of position values
--List of rotation values
--List of bone ids
---Index of bone id in list matches up to index in scale, position and rotation lists so that the properties can be associated with the bone
--Time value at which the keyframe will be played
--Method to return the scale value of a bone as determined by the bone id passed in
--Method to return the position value of a bone as determined by the bone id passed in
--Method to return the rotation value of a bone as determined by the bone id passed in
このコードを解析するために作成した関連メソッドは次のとおりです。
public static Skeleton parseSkeletonFile(String location) throws FileNotFoundException, IOException
{
try
{
Skeleton skelToReturn = null;
File fileToLoad = new File(location);
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
Document doc = dBuilder.parse(fileToLoad);
doc.getDocumentElement().normalize();
NodeList skelList = doc.getElementsByTagName("skeleton");
for (int counter = 0; counter < skelList.getLength(); counter++)
{
Node skelNode = skelList.item(counter);
Element skelElement = (Element)skelNode;
skelToReturn = new Skeleton(skelElement.getAttribute("name"));
NodeList allNodes = skelNode.getChildNodes();
for (int counter2 = 0; counter2 < allNodes.getLength(); counter2++)
{
Node currentNode = allNodes.item(counter2);
if (currentNode.getNodeName() == "bones")
{
NodeList bonesNL = currentNode.getChildNodes();
for (int counter3 = 0; counter3 < bonesNL.getLength(); counter3++)
{
Node currentBone = bonesNL.item(counter3);
Element currentBoneE = (Element)currentBone;
Bone bone = new Bone(
currentBoneE.getAttribute("name"),
new Point(
Float.valueOf(currentBoneE.getElementsByTagName("position").item(0).getTextContent().split(",")[0]),
Float.valueOf(currentBoneE.getElementsByTagName("position").item(0).getTextContent().split(",")[1])
),
Float.valueOf(currentBoneE.getElementsByTagName("rotation").item(0).getTextContent()),
Float.valueOf(currentBoneE.getElementsByTagName("scale").item(0).getTextContent()),
Integer.valueOf(currentBoneE.getAttribute("id"))
);
bone.setImage(new Image(currentBoneE.getAttribute("textureLoc")));
skelToReturn.bones.add(bone);
}
}
if (currentNode.getNodeName() == "animation")
{
NodeList animsNL = currentNode.getChildNodes();
for (int counter4 = 0; counter4 < animsNL.getLength(); counter4++)
{
Node currentAnim = animsNL.item(counter4);
Element currentAnimE = (Element)currentAnim;
Animation animation = new Animation(currentAnimE.getAttribute("name"));
NodeList keyframesNL = currentAnim.getChildNodes();
for (int counter5 = 0; counter5 < keyframesNL.getLength(); counter5++)
{
Node currentKeyframe = keyframesNL.item(counter5);
Element currentKeyframeE = (Element)currentKeyframe;
Keyframe keyframe = new Keyframe(Integer.valueOf(currentKeyframeE.getAttribute("frameToPlay")));
NodeList kfBonesNL = currentKeyframe.getChildNodes();
for (int counter6 = 0; counter6 < kfBonesNL.getLength(); counter6++)
{
Node currentBoneTrans = kfBonesNL.item(counter6);
Element currentBoneTransE = (Element)currentBoneTrans;
keyframe.boneIDs.add(Integer.valueOf(currentBoneTransE.getAttribute("id")));
keyframe.positions.add(new Point(Float.valueOf(currentBoneTransE.getAttribute("transPosition").split(",")[0]), Float.valueOf(currentBoneTransE.getAttribute("transPosition").split(",")[1])));
keyframe.rotations.add(Float.valueOf(currentBoneTransE.getAttribute("transRotation")));
keyframe.scales.add(Float.valueOf(currentBoneTransE.getAttribute("transScale")));
}
animation.keyframes.add(keyframe);
animation.frames.add(keyframe.getTimeToBePlayed());
}
skelToReturn.animations.add(animation);
skelToReturn.animationNames.add(animation.name);
}
}
}
}
if (skelToReturn != null)
return skelToReturn;
else
return null;
} catch (Exception e)
{
e.printStackTrace();
}
return null;
}
パーサーをそのままテストするために使用しているコードのスニペットを次に示します。
AnimationSystem animator = new AnimationSystem();
try {
skeleton = animator.parseSkeletonFile(getClass().getResource("SkeletonTest.xml").getPath());
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (skeleton != null)
{
System.out.println(skeleton.name);
}
これは、Slick2D で構築された単純なゲームの init メソッドにあります。読み込もうとしているファイル SkeletonTest.xml は、解析メソッドを呼び出しているクラスと同じパッケージにあります。その内容は単純です:
<skeleton name="TestSkeleton">
</skeleton>
そして、これは私が受け取っているエラーメッセージ全体です:
java.io.FileNotFoundException: D:\Eclipse%20Workspace\2dSkeletalAnimator\bin\org\jason\animatorTests\SkeletonTest.xml (The system cannot find the path specified)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(Unknown Source)
at java.io.FileInputStream.<init>(Unknown Source)
at sun.net.www.protocol.file.FileURLConnection.connect(Unknown Source)
at sun.net.www.protocol.file.FileURLConnection.getInputStream(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown Source)
at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source)
at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source)
at javax.xml.parsers.DocumentBuilder.parse(Unknown Source)
at org.jason.skeletalanimator.AnimationSystem.parseSkeletonFile(AnimationSystem.java:42)
at org.jason.animatorTests.AnimatorTest1.init(AnimatorTest1.java:32)
at org.newdawn.slick.AppGameContainer.setup(AppGameContainer.java:433)
at org.newdawn.slick.AppGameContainer.start(AppGameContainer.java:357)
at org.jason.animatorTests.AnimatorTest1.main(AnimatorTest1.java:65)
やや一般的な質問と必要な背景情報の量で申し訳ありませんが、解析メソッドの本体を書き直すことなく、これを機能させるためにあらゆることを試みました。あの混乱は二度と繰り返さないほうがいい。そこで、XML スタイル ファイルからデータを読み込んだ経験のある人が、ここで私を助けてくれることを願っています。事前に時間を割いてくれた人に感謝します。