Recent

Author Topic: Using the Castle Game Engine Without Any Prior Coding Experience  (Read 4843 times)

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Using the Castle Game Engine Without Any Prior Coding Experience
« on: February 20, 2024, 09:56:09 pm »
Hi everyone,

I didn't specify I was autsitic and took directions very literally/read the words exactly as they were, which might have been part of the problem,

but I did specify I was approaching the usage of a free game engine, called "Castle Game Engine" that can be downloaded by Googling "Castle Game Engine Download", from the perspective of an absolute beginner that doesn't know the ins and outs of the particular language's details, but knows in general that "var" means variable, or "begin" and "end" begin and end a specific block of code, and they need to happen for the code to work in multi line blocks.

On another thread in the Lazarus forums, but not the beginner's section, which is probably also part of the problem, because they assume you aren't an absolute beginner but know how to figure things out by yourself by reading the manual,

I was told to read the manual for help, rather than actually being given the help directly.

This is fine given that it's not aimed at beginners but rather people who know Pascal well already, but the responses were very beginner unfriendly like "this thread is pure comedy gold" or "don't change the code then complain when it doesn't work", even though I would point out using examples how the changes were done to the best of my ability by following what the example projects' code suggests I should do.

Does anyone with professional insider knowledge/experience with the Castle Game Engine want to help me, as it is clear that they just want to keep referring me to the manual/telling me to RTFM rather than actually properly walking me through it or spelling anything out themself?

In particular, I need help with this piece of code, about drawing a raycast from a player transform; I need the player's coordinates the way they are to control the location precisely of the ray being drawn to the ground, but again cause they assumed I had enough insider knowledge they never bothered to show me how directly on the other thread, but kept telling me to teach everything to myself:

Original example's code for gamemymesh.pas, note it compiles fine exactly as it is:

Code: Pascal  [Select][+][-]
  1. {
  2.   Copyright 2023-2023 Michalis Kamburelis.
  3.  
  4.   This file is part of "Castle Game Engine".
  5.  
  6.   "Castle Game Engine" is free software; see the file COPYING.txt,
  7.   included in this distribution, for details about the copyright.
  8.  
  9.   "Castle Game Engine" is distributed in the hope that it will be useful,
  10.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12.  
  13.   ----------------------------------------------------------------------------
  14. }
  15.  
  16. { TCastleTransform descendant that renders unlit mesh using TCastleRenderUnlitMesh. }
  17. unit GameMyMesh;
  18.  
  19. interface
  20.  
  21. uses SysUtils,
  22.   CastleRenderPrimitives, CastleBoxes, CastleTransform;
  23.  
  24. type
  25.   { Mesh rendered using TCastleRenderUnlitMesh, not using TCastleScene.
  26.     This is not generally advised, TCastleScene has much more features
  27.     and is easier to use than TCastleRenderUnlitMesh.
  28.     We do this only to test TCastleRenderUnlitMesh with rcForceFixedFunction here. }
  29.   TMyMesh = class(TCastleTransform)
  30.   strict private
  31.     Mesh: TCastleRenderUnlitMesh;
  32.   public
  33.     procedure LocalRender(const Params: TRenderParams); override;
  34.     procedure GLContextClose; override;
  35.     function LocalBoundingBox: TBox3D; override;
  36.   end;
  37.  
  38. implementation
  39.  
  40. uses CastleVectors, CastleRenderContext, CastleColors, CastleGLUtils;
  41.  
  42. { TMyMesh -------------------------------------------------------------------- }
  43.  
  44. function TMyMesh.LocalBoundingBox: TBox3D;
  45. begin
  46.   Result := inherited;
  47.   Result := Result + Box3D(
  48.     Vector3(-1, -1, -1),
  49.     Vector3( 1,  1,  1)
  50.   );
  51. end;
  52.  
  53. procedure TMyMesh.LocalRender(const Params: TRenderParams);
  54.  
  55.   procedure CreateMesh;
  56.   begin
  57.     Mesh := TCastleRenderUnlitMesh.Create(true);
  58.     Mesh.SetVertexes([
  59.       Vector4(-1, -1, -1, 1),
  60.       Vector4( 1, -1, -1, 1),
  61.       Vector4( 1,  1, -1, 1),
  62.       Vector4(-1,  1, -1, 1),
  63.  
  64.       Vector4(-1, -1,  1, 1),
  65.       Vector4( 1, -1,  1, 1),
  66.       Vector4( 1,  1,  1, 1),
  67.       Vector4(-1,  1,  1, 1)
  68.     ], false);
  69.     Mesh.SetIndexes([
  70.       // line loop on Z = -1
  71.       0, 1,
  72.       1, 2,
  73.       2, 3,
  74.       3, 0,
  75.  
  76.       // line loop on Z = 1
  77.       4, 5,
  78.       5, 6,
  79.       6, 7,
  80.       7, 4,
  81.  
  82.       // connect Z = -1 with Z = 1
  83.       0, 4,
  84.       1, 5,
  85.       2, 6,
  86.       3, 7
  87.     ]);
  88.     Mesh.Color := Yellow;
  89.   end;
  90.  
  91. var
  92.   SavedDepthTest: Boolean;
  93.   SavedLineWidth: Single;
  94. begin
  95.   inherited;
  96.   SavedDepthTest := RenderContext.DepthTest;
  97.   SavedLineWidth := RenderContext.LineWidth;
  98.   RenderContext.DepthTest := true;
  99.   RenderContext.LineWidth := 5;
  100.  
  101.   if Mesh = nil then
  102.     CreateMesh;
  103.   Mesh.ModelViewProjection := RenderContext.ProjectionMatrix *
  104.     Params.RenderingCamera.CurrentMatrix * WorldTransform;
  105.   Mesh.Render(pmLines);
  106.  
  107.   RenderContext.DepthTest := SavedDepthTest;
  108.   RenderContext.LineWidth := SavedLineWidth;
  109. end;
  110.  
  111. procedure TMyMesh.GLContextClose;
  112. begin
  113.   FreeAndNil(Mesh);
  114.   inherited;
  115. end;
  116.  
  117. end.

Code that I want it changed to, but get a crash at line 64 in particular:

Code: Pascal  [Select][+][-]
  1. {
  2.   Copyright 2023-2023 Michalis Kamburelis.
  3.  
  4.   This file is part of "Castle Game Engine".
  5.  
  6.   "Castle Game Engine" is free software; see the file COPYING.txt,
  7.   included in this distribution, for details about the copyright.
  8.  
  9.   "Castle Game Engine" is distributed in the hope that it will be useful,
  10.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12.  
  13.   ----------------------------------------------------------------------------
  14. }
  15.  
  16. { TCastleTransform descendant that renders unlit mesh using TCastleRenderUnlitMesh. }
  17. unit GameMyMesh;
  18.  
  19. interface
  20.  
  21. uses SysUtils,
  22.   CastleRenderPrimitives, CastleBoxes, CastleTransform;
  23.  
  24. type
  25.   { Mesh rendered using TCastleRenderUnlitMesh, not using TCastleScene.
  26.     This is not generally advised, TCastleScene has much more features
  27.     and is easier to use than TCastleRenderUnlitMesh.
  28.     We do this only to test TCastleRenderUnlitMesh with rcForceFixedFunction here. }
  29.   TMyMesh = class(TCastleTransform)
  30.   published
  31.     AvatarTransform: TCastleTransform;
  32.   strict private
  33.     Mesh: TCastleRenderUnlitMesh;
  34.   public
  35.     procedure LocalRender(const Params: TRenderParams); override;
  36.     procedure GLContextClose; override;
  37.     function LocalBoundingBox: TBox3D; override;
  38.   end;
  39.  
  40. implementation
  41.  
  42. uses CastleVectors, CastleRenderContext, CastleColors, CastleGLUtils, GameViewPlay;
  43.  
  44. { TMyMesh -------------------------------------------------------------------- }
  45.  
  46. function TMyMesh.LocalBoundingBox: TBox3D;
  47. begin
  48.   Result := inherited;
  49.   Result := Result + Box3D(
  50.     Vector3(-1, -1, -1),
  51.     Vector3( 1,  1,  1)
  52.   );
  53. end;
  54.  
  55. procedure TMyMesh.LocalRender(const Params: TRenderParams);
  56.  
  57.   procedure CreateMesh(AvatarTransform: TCastleTransform);
  58.    var
  59.      PlayerAvatarTransform: TCastleTransform;
  60.    begin
  61.      PlayerAvatarTransform := AvatarTransform;
  62.      Mesh := TCastleRenderUnlitMesh.Create(true);
  63.      Mesh.SetVertexes([
  64.        Vector4(0.0, AvatarTransform.Translation.Y, 0.0, 1.0),
  65.        Vector4(0.0, (AvatarTransform.Translation.Y - AvatarTransform.Direction.Y), 0.0, 1.0)
  66.      ], false);
  67.      Mesh.SetIndexes([
  68.        // line loop on Z = -1
  69.        0, 1
  70.      ]);
  71.      Mesh.Color := Yellow;
  72.    end;
  73.  
  74. var
  75.   SavedDepthTest: Boolean;
  76.   SavedLineWidth: Single;
  77. begin
  78.   inherited;
  79.   SavedDepthTest := RenderContext.DepthTest;
  80.   SavedLineWidth := RenderContext.LineWidth;
  81.   RenderContext.DepthTest := true;
  82.   RenderContext.LineWidth := 5;
  83.  
  84.   if Mesh = nil then
  85.     CreateMesh(AvatarTransform);
  86.   Mesh.ModelViewProjection := RenderContext.ProjectionMatrix *
  87.     Params.RenderingCamera.CurrentMatrix * WorldTransform;
  88.   Mesh.Render(pmLines);
  89.  
  90.   RenderContext.DepthTest := SavedDepthTest;
  91.   RenderContext.LineWidth := SavedLineWidth;
  92. end;
  93.  
  94. procedure TMyMesh.GLContextClose;
  95. begin
  96.   FreeAndNil(Mesh);
  97.   inherited;
  98. end;
  99.  
  100. end.

Can someone please kindly and patiently spell out for me where the code is wrong, rather than telling me I need to read the manual more without explaining what rule I actually broke?

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #1 on: February 20, 2024, 09:59:20 pm »
Because I know even when I read the API, and it states Vector4 is a 4D vector with 4 floating point values, and made sure to do that, I still get the crash.

I assume it's because I didn't include something else necessary, but it's not obvious at all to a beginner who just follows the book exactly as it is like me.

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #2 on: February 21, 2024, 12:46:44 am »
Okay, I made a well-educated guess that perhaps I was using the object's value rather than the object's name to access the variable property, which is not how you should normally do it if I remember that rule correctly from the manual as well as other coding languages,

for example you never say "x equals 5, add 5 to 5" to add 5 to x, but rather "x equals 5, add 5 to x", in fake code obviously and not the syntax of a real language, to make a general point.

However, even after I made that fix I get the same crash, despite having used the correct number of inputs for the Vector4 and the correct syntax to access the object's property, namely "object.property.subproperty", cause I wanted to use only one coordinate of the translation/location and not all 3 at once to make the raycast drawing super precise.

Again, I am assuming it is because I didn't include something else extra, but given that I declared the variable using the "var" keyword, and initialized it with a specific value, in the same exact way as I would do for any variable that the examples show me, I am having a hard time understanding what that something else extra is.

I guessed in the other thread that I needed it to be a class rather than normal variable, but was accused of making random guesses rather than understanding the code correctly, when everyone in that thread was forcing me to make random guesses, because no one was willing to actually spell out anything for me save for a couple of helpful posts from others.

michalis

  • Full Member
  • ***
  • Posts: 143
    • Castle Game Engine
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #3 on: February 21, 2024, 03:07:06 am »
Quote
Can someone please kindly and patiently spell out for me where the code is wrong, rather than telling me I need to read the manual more without explaining what rule I actually broke?

I did point out the problem in the old thread:

Initialize the field "AvatarTransform" of the instance of class "TMyMesh". It is not initialized now.

See my post on https://forum.lazarus.freepascal.org/index.php/topic,66238.msg507530.html#msg507530 .

Note that if someone reads only this forum thread, they don't really have enough information to help you. You show your modified GameMyMesh unit source code, but the problem is in unit using it, GameViewPlay. Where you, to say it again, do not initialize the field "AvatarTransform" of the instance of class "TMyMesh" .

cdbc

  • Hero Member
  • *****
  • Posts: 1646
    • http://www.cdbc.dk
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #4 on: February 21, 2024, 08:56:17 am »
Hi
@Michalis: in 'GameViewPlay'
Quote
Initialize the field "AvatarTransform" of the instance of class "TMyMesh". It is not initialized now.
In method "procedure TViewPlay.Start;", just after:
Code: Pascal  [Select][+][-]
  1. DebugAvatar := TDebugTransform.Create(FreeAtStop);
I'd put this line:
Code: Pascal  [Select][+][-]
  1. AvatarTransform:= TAvatarTransform.Create(FreeAtStop);
  2. // or if it's autocreated then something like:
  3. AvatarTransform.Init({someparams if required});
  4. // or whatever the initializing bit is called
So that the 'AvatarTransform' is created or initialized, before its first use...
Otherwise I can't see what you mean by "Initialize"?!?
Sorry, just trying to understand.
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

michalis

  • Full Member
  • ***
  • Posts: 143
    • Castle Game Engine
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #5 on: February 21, 2024, 09:30:41 am »
Hi
@Michalis: in 'GameViewPlay'
Quote
Initialize the field "AvatarTransform" of the instance of class "TMyMesh". It is not initialized now.
In method "procedure TViewPlay.Start;", just after:
Code: Pascal  [Select][+][-]
  1. DebugAvatar := TDebugTransform.Create(FreeAtStop);
I'd put this line:
Code: Pascal  [Select][+][-]
  1. AvatarTransform:= TAvatarTransform.Create(FreeAtStop);
  2. // or if it's autocreated then something like:
  3. AvatarTransform.Init({someparams if required});
  4. // or whatever the initializing bit is called
So that the 'AvatarTransform' is created or initialized, before its first use...
Otherwise I can't see what you mean by "Initialize"?!?
Sorry, just trying to understand.
Regards Benny

That's not what I meant, but I know it may be confusing since GameViewPlay is not included in this thread. I'm basing my judgement on the last version of that file I saw from OP, in https://forum.lazarus.freepascal.org/index.php/topic,66238.msg507353.html#msg507353 .

There, the "MyMesh" instance is created ("MyMesh := TMyMesh.Create(FreeAtStop)") but it's field "AvatarTransform" ("MyMesh.AvatarTransform") is never set to anything.

In consequence, the code inside GameMyMesh crashes. That code tries to do "Vector4(0.0, AvatarTransform.Translation.Y, 0.0, 1.0)" but at this point "AvatarTransform" inside TMyMesh is not initialized. It is nil (zero), which means that accessing it will cause "Access Violation", which is exactly what the poster shows in the screenshot on https://forum.lazarus.freepascal.org/index.php/topic,66238.msg507353.html#msg507353 .

The solution is to set "MyMesh.AvatarTransform" to the value of "AvatarTransform" field in TViewPlay. The value of field "AvatarTransform" inside TViewPlay should be valid already (based on what I've seen in the design), CGE will set the published field "AvatarTransform"  in TViewPlay automatically. So one just needs to use this and assign it to "MyMesh.AvatarTransform".


cdbc

  • Hero Member
  • *****
  • Posts: 1646
    • http://www.cdbc.dk
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #6 on: February 21, 2024, 10:07:47 am »
Hi
Right, here goes...:
Code: Pascal  [Select][+][-]
  1. procedure TViewPlay.Start;
  2. var
  3.   SoldierScene: TCastleScene;
  4.   Enemy: TEnemy;
  5.   I: Integer;
  6.   MyMesh: TMyMesh;
  7. begin
  8.   inherited;
  9.  
  10.   MyMesh := TMyMesh.Create(FreeAtStop);
  11.   ///// This could be the solution \\\\\
  12.   MyMesh.AvatarTransform:= AvatarTransform;
  13.   ///// end of solution(to this problem) \\\\\
  14.   AvatarTransform.Add(MyMesh);
  15.   // etc...
  16.  
That is, if I understand correctly  %)
Thanks Michalis
Regards Benny
If it ain't broke, don't fix it ;)
PCLinuxOS(rolling release) 64bit -> KDE5 -> FPC 3.2.2 -> Lazarus 2.2.6 up until Jan 2024 from then on it's: KDE5/QT5 -> FPC 3.3.1 -> Lazarus 3.0

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #7 on: February 21, 2024, 11:33:24 am »
Oh... now I understand. I created the MyMesh and added the transform, but I didn't initialize the transform property of the MyMesh before adding it.

Thank you guys so much for the direct help!

Again, however, direct help is what I am seeking, rather than telling me I made a mistake without wanting to explain what the mistake actually was, especially because I can explain that I know what I am talking about, IE that I know I need to type and assign variables in a specific way and stuff like that.

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #8 on: February 21, 2024, 11:37:56 am »
Now it compiles perfectly, but the catch is that now the model isn't drawn as well as the mesh; however, I am assuming it is because I didn't include the rest of the code from the example as it was, for example I didn't include any lines at all from gameviewmain.pas in test_rendering_opengl_capabilities, which I would assume it's important if it's included - I know obviously you guys wouldn't include any code if it was redundant or unnecessary,

So I know like most issues this one is my fault 100%.

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #9 on: February 21, 2024, 01:17:56 pm »
In particular, cause I didn't include the details of the drawable image, and I would assume it is required to actually draw the mesh; I will see what happens later, as I am busy with errands right now.

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #10 on: February 22, 2024, 12:32:26 am »
Due to the 20000 character length limitation, I am going to need to make multiple posts in a row to elaborate on the full problem I am having, with every detail needed:

Okay, so I tried to follow the directions to the letter of the original example, but being mindful of not just mindlessly copy pasting the example, but making appropriate name changes and paying attention to the order the code was in in the original example, and doing the same thing in mine:

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #11 on: February 22, 2024, 12:33:30 am »
GameViewMain.pas from test_rendering_opengl_capabilities, unchanged and in full, making sure to not omit lines for no good reason:

Code: Pascal  [Select][+][-]
  1. {
  2.   Copyright 2023-2023 Michalis Kamburelis.
  3.  
  4.   This file is part of "Castle Game Engine".
  5.  
  6.   "Castle Game Engine" is free software; see the file COPYING.txt,
  7.   included in this distribution, for details about the copyright.
  8.  
  9.   "Castle Game Engine" is distributed in the hope that it will be useful,
  10.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12.  
  13.   ----------------------------------------------------------------------------
  14. }
  15.  
  16. { Main view, where most of the application logic takes place. }
  17. unit GameViewMain;
  18.  
  19. interface
  20.  
  21. uses Classes,
  22.   CastleVectors, CastleComponentSerialize, CastleScene, CastleCameras,
  23.   CastleUIControls, CastleControls, CastleKeysMouse, CastleGLImages, CastleViewport,
  24.   CastleTransform, CastleGLUtils;
  25.  
  26. type
  27.   { Main view, where most of the application logic takes place. }
  28.   TViewMain = class(TCastleView)
  29.   published
  30.     { Components designed using CGE editor.
  31.       These fields will be automatically initialized at Start. }
  32.     LabelFps, LabelGlInformation: TCastleLabel;
  33.     ScenePhong: TCastleScene;
  34.     MainViewport: TCastleViewport;
  35.     WalkNavigation: TCastleWalkNavigation;
  36.     UnlitMeshParent: TCastleTransform;
  37.   private
  38.     DrawableImage: TDrawableImage;
  39.   public
  40.     constructor Create(AOwner: TComponent); override;
  41.     procedure Start; override;
  42.     procedure Stop; override;
  43.     procedure Update(const SecondsPassed: Single; var HandleInput: Boolean); override;
  44.     procedure RenderOverChildren; override;
  45.   end;
  46.  
  47. var
  48.   ViewMain: TViewMain;
  49.  
  50. implementation
  51.  
  52. uses SysUtils,
  53.   CastleLoadGltf, CastleRectangles, CastleImages,
  54.   CastleBoxes, CastleColors, CastleRenderContext, CastleUtils, X3DLoad,
  55.   GameMyMesh;
  56.  
  57. { TViewMain ----------------------------------------------------------------- }
  58.  
  59. constructor TViewMain.Create(AOwner: TComponent);
  60. begin
  61.   inherited;
  62.   DesignUrl := 'castle-data:/gameviewmain.castle-user-interface';
  63. end;
  64.  
  65. procedure TViewMain.Start;
  66. var
  67.   MyMesh: TMyMesh;
  68. begin
  69.   inherited;
  70.  
  71.   LabelGlInformation.Caption :=
  72.     'OpenGL capabilities requested: ' + CapabilitiesStr[TGLFeatures.RequestCapabilities] + NL +
  73.     '(see log for the details about context)';
  74.  
  75.   { Load ScenePhong.Url with temporary GltfForcePhongMaterials set to true.
  76.     We want to test Phong shading works in ForceFixedFunction too. }
  77.   GltfForcePhongMaterials := true;
  78.   ScenePhong.Url := 'castle-data:/sample_3d.gltf';
  79.   GltfForcePhongMaterials := false;
  80.  
  81.   DrawableImage := TDrawableImage.Create('castle-data:/texture_alpha.png');
  82.  
  83.   MyMesh := TMyMesh.Create(FreeAtStop);
  84.   UnlitMeshParent.Add(MyMesh);
  85.  
  86.   MainViewport.AddScreenEffect(LoadNode('castle-data:/screen_effect_frame.x3dv'));
  87. end;
  88.  
  89. procedure TViewMain.Stop;
  90. begin
  91.   FreeAndNil(DrawableImage);
  92.   inherited;
  93. end;
  94.  
  95. procedure TViewMain.Update(const SecondsPassed: Single; var HandleInput: Boolean);
  96. begin
  97.   inherited;
  98.   { This virtual method is executed every frame (many times per second). }
  99.   Assert(LabelFps <> nil, 'If you remove LabelFps from the design, remember to remove also the assignment "LabelFps.Caption := ..." from code');
  100.   LabelFps.Caption := 'FPS: ' + Container.Fps.ToString;
  101.  
  102.   WalkNavigation.MouseLook := buttonRight in Container.MousePressed;
  103. end;
  104.  
  105. procedure TViewMain.RenderOverChildren;
  106. begin
  107.   inherited;
  108.   { Do some direct drawing, to test this API works with ForceFixedFunction too. }
  109.   DrawRectangle(FloatRectangle(10, 10, 50, 50), Green);
  110.   DrawableImage.Draw(100, 10);
  111.  
  112.   { Render DrawableImage again, forcing alpha testing }
  113.   DrawableImage.Alpha := acTest;
  114.   DrawableImage.Draw(400, 10);
  115.   DrawableImage.Alpha := acAuto;
  116.  
  117.   FallbackFont.Print(700, 10, Red, 'Another sample text');
  118. end;
  119.  
  120. end.

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #12 on: February 22, 2024, 12:34:49 am »
Using the code in my game that I want to make, a kid friendly third person adventure platformer thing using the "third person navigation" example that also comes with the Castle game engine:

Code: Pascal  [Select][+][-]
  1. {
  2.   Copyright 2020-2023 Michalis Kamburelis.
  3.  
  4.   This file is part of "Castle Game Engine".
  5.  
  6.   "Castle Game Engine" is free software; see the file COPYING.txt,
  7.   included in this distribution, for details about the copyright.
  8.  
  9.   "Castle Game Engine" is distributed in the hope that it will be useful,
  10.   but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  12.  
  13.   ----------------------------------------------------------------------------
  14. }
  15.  
  16. { Main "playing game" view, where most of the game logic takes place. }
  17. unit GameViewPlay;
  18.  
  19. { Unit for GameMesh renderer features. }
  20.  
  21. {.$define CASTLE_UNFINISHED_CHANGE_TRANSFORMATION_BY_FORCE}
  22.  
  23. interface
  24.  
  25. uses Classes,
  26.   CastleComponentSerialize, CastleUIControls, CastleControls,
  27.   CastleKeysMouse, CastleViewport, CastleScene, CastleVectors, CastleCameras,
  28.   CastleTransform, CastleInputs, CastleThirdPersonNavigation, CastleDebugTransform,
  29.   CastleSceneCore, SysUtils, CastleRenderPrimitives, CastleBoxes,
  30.   GameEnemy, CastleGLImages, CastleGLUtils;
  31.  
  32. type
  33.  
  34.   { Main "playing game" view, where most of the game logic takes place. }
  35.   TViewPlay = class(TCastleView)
  36.   published
  37.     { Components designed using CGE editor.
  38.       These fields will be automatically initialized at Start. }
  39.     LabelFps, LabelGlInformation: TCastleLabel;
  40.     MainViewport: TCastleViewport;
  41.     WalkNavigation: TCastleWalkNavigation;
  42.     AvatarTransform: TCastleTransform;
  43.     SandyLegs, SandyHead, SandyTorso, SceneLevel, ScenePhong: TCastleScene;
  44.     AvatarRigidBody: TCastleRigidBody;
  45.     CheckboxCameraFollows: TCastleCheckbox;
  46.     CheckboxAimAvatar: TCastleCheckbox;
  47.     CheckboxDebugAvatarColliders: TCastleCheckbox;
  48.     CheckboxImmediatelyFixBlockedCamera: TCastleCheckbox;
  49.     SliderAirRotationControl: TCastleFloatSlider;
  50.     SliderAirMovementControl: TCastleFloatSlider;
  51.     ButtonChangeTransformationAuto,
  52.     ButtonChangeTransformationDirect,
  53.     ButtonChangeTransformationVelocity,
  54.     ButtonChangeTransformationForce: TCastleButton;
  55.   private
  56.     { Drawable Image setup }
  57.     DrawableImage: TDrawableImage;
  58.     { Enemies behaviors }
  59.     Enemies: TEnemyList;
  60.  
  61.     DebugAvatar: TDebugTransform;
  62.     { Change things after ThirdPersonNavigation.ChangeTransformation changed. }
  63.     procedure UpdateAfterChangeTransformation;
  64.     procedure ChangeCheckboxCameraFollows(Sender: TObject);
  65.     procedure ChangeCheckboxAimAvatar(Sender: TObject);
  66.     procedure ChangeCheckboxDebugAvatarColliders(Sender: TObject);
  67.     procedure ChangeCheckboxImmediatelyFixBlockedCamera(Sender: TObject);
  68.     procedure ChangeAirRotationControl(Sender: TObject);
  69.     procedure ChangeAirMovementControl(Sender: TObject);
  70.     procedure ClickChangeTransformationAuto(Sender: TObject);
  71.     procedure ClickChangeTransformationDirect(Sender: TObject);
  72.     procedure ClickChangeTransformationVelocity(Sender: TObject);
  73.     procedure ClickChangeTransformationForce(Sender: TObject);
  74.   public
  75.     constructor Create(AOwner: TComponent); override;
  76.     procedure Start; override;
  77.     procedure Stop; override;
  78.     procedure Update(const SecondsPassed: Single; var HandleInput: Boolean); override;
  79.     procedure RenderOverChildren; override;
  80.     function Press(const Event: TInputPressRelease): Boolean; override;
  81.     function Release(const Event: TInputPressRelease): Boolean; override;
  82.   end;
  83.  
  84.   TMyThirdPersonNavigation = class(TCastleThirdPersonNavigation)
  85.   protected
  86.     procedure SetAnimation(const AnimationNames: array of String); override;
  87.   end;
  88.  
  89. var
  90.   ViewPlay: TViewPlay;
  91.   MyThirdPersonNavigation: TMyThirdPersonNavigation;
  92.   GroundRayCast: TPhysicsRayCastResult;
  93.   StandOnGround: Boolean;
  94.   SandyInRunning: Boolean;
  95.   MaxDistance: Integer;
  96.  
  97. implementation
  98.  
  99. uses Math, StrUtils, CastleRenderContext, CastleColors,
  100.   CastleSoundEngine, CastleLog, CastleStringUtils, CastleFilesUtils,
  101.   GameViewMenu, GameMyMesh, CastleLoadGltf, CastleRectangles,
  102.   CastleImages, CastleUtils, X3DLoad;
  103.  
  104. { TViewPlay ----------------------------------------------------------------- }
  105.  
  106. constructor TViewPlay.Create(AOwner: TComponent);
  107. begin
  108.   inherited;
  109.   DesignUrl := 'castle-data:/gameviewplay.castle-user-interface';
  110. end;
  111.  
  112. procedure TMyThirdPersonNavigation.SetAnimation(const AnimationNames: array of String);
  113. begin
  114.  
  115. end;
  116.  
  117. procedure TViewPlay.Start;
  118. var
  119.   SoldierScene: TCastleScene;
  120.   Enemy: TEnemy;
  121.   I: Integer;
  122.   MyMesh: TMyMesh;
  123. begin
  124.   inherited;
  125.  
  126.   AvatarTransform := AvatarTransform.Create(FreeAtStop);
  127.  
  128.   LabelGlInformation.Caption :=
  129.     'OpenGL capabilities requested: ' + CapabilitiesStr[TGLFeatures.RequestCapabilities] + NL +
  130.     '(see log for the details about context)';
  131.  
  132.   { Load ScenePhong.Url with temporary GltfForcePhongMaterials set to true.
  133.     We want to test Phong shading works in ForceFixedFunction too. }
  134.   GltfForcePhongMaterials := true;
  135.   ScenePhong.Url := 'castle-data:/sample_3d.gltf';
  136.   GltfForcePhongMaterials := false;
  137.  
  138.   DrawableImage := TDrawableImage.Create('castle-data:/texture_alpha.png');
  139.  
  140.   MyMesh := TMyMesh.Create(FreeAtStop);
  141.  
  142.   MyMesh.AvatarTransform := AvatarTransform;
  143.  
  144.   AvatarTransform.Add(MyMesh);
  145.  
  146.   MainViewport.AddScreenEffect(LoadNode('castle-data:/screen_effect_frame.x3dv'));
  147.  
  148.   MyThirdPersonNavigation := TMyThirdPersonNavigation.Create(FreeAtStop);
  149.   { Create TEnemy instances, add them to Enemies list }
  150.   Enemies := TEnemyList.Create(true);
  151.      for I := 1 to 4 do
  152.      begin
  153.         SoldierScene := DesignedComponent('SceneSoldier' + IntToStr(I)) as TCastleScene;
  154.         { Below using nil as Owner of TEnemy, as the Enemies list already "owns"
  155.         instances of this class, i.e. it will free them. }
  156.         Enemy := TEnemy.Create(nil);
  157.         SoldierScene.AddBehavior(Enemy);
  158.         Enemies.Add(Enemy);
  159.      end;
  160.  
  161.   { synchronize state -> UI }
  162.   SliderAirRotationControl.Value := MyThirdPersonNavigation.AirRotationControl;
  163.   SliderAirMovementControl.Value := MyThirdPersonNavigation.AirMovementControl;
  164.   UpdateAfterChangeTransformation;
  165.  
  166.   CheckboxCameraFollows.OnChange := {$ifdef FPC}@{$endif} ChangeCheckboxCameraFollows;
  167.   CheckboxAimAvatar.OnChange := {$ifdef FPC}@{$endif} ChangeCheckboxAimAvatar;
  168.   CheckboxDebugAvatarColliders.OnChange := {$ifdef FPC}@{$endif} ChangeCheckboxDebugAvatarColliders;
  169.   CheckboxImmediatelyFixBlockedCamera.OnChange := {$ifdef FPC}@{$endif} ChangeCheckboxImmediatelyFixBlockedCamera;
  170.   SliderAirRotationControl.OnChange := {$ifdef FPC}@{$endif} ChangeAirRotationControl;
  171.   SliderAirMovementControl.OnChange := {$ifdef FPC}@{$endif} ChangeAirMovementControl;
  172.   ButtonChangeTransformationAuto.OnClick := {$ifdef FPC}@{$endif} ClickChangeTransformationAuto;
  173.   ButtonChangeTransformationDirect.OnClick := {$ifdef FPC}@{$endif} ClickChangeTransformationDirect;
  174.   ButtonChangeTransformationVelocity.OnClick := {$ifdef FPC}@{$endif} ClickChangeTransformationVelocity;
  175.   ButtonChangeTransformationForce.OnClick := {$ifdef FPC}@{$endif} ClickChangeTransformationForce;
  176.  
  177.   {$ifndef CASTLE_UNFINISHED_CHANGE_TRANSFORMATION_BY_FORCE}
  178.   { Hide UI to test ChangeTransformation = ctForce, it is not finished now,
  179.     not really useful for normal usage. }
  180.   ButtonChangeTransformationForce.Exists := false;
  181.   {$endif}
  182.  
  183.   { This configures SceneAvatar.Middle point, used for shooting.
  184.     In case of old physics (ChangeTransformation = ctDirect) this is also the center
  185.     of SceneAvatar.CollisionSphereRadius. }
  186.   // SceneAvatar.MiddleHeight := 0.9;
  187.  
  188.   { Configure some parameters of old simple physics,
  189.     these only matter when SceneAvatar.Gravity = true.
  190.     Don't use these deprecated things if you don't plan to use ChangeTransformation = ctDirect! }
  191.   // SceneAvatar.GrowSpeed := 10.0;
  192.   // SceneAvatar.FallSpeed := 10.0;
  193.   { When avatar collides as sphere it can climb stairs,
  194.     because legs can temporarily collide with objects. }
  195.   // SceneAvatar.CollisionSphereRadius := 0.5;
  196.  
  197.   { Visualize SceneAvatar bounding box, sphere, middle point, direction etc. }
  198.   DebugAvatar := TDebugTransform.Create(FreeAtStop);
  199.   // DebugAvatar.Parent := SceneAvatar;
  200.  
  201.   { Configure MyThirdPersonNavigation keys (for now, we don't expose doing this in CGE editor). }
  202.   MyThirdPersonNavigation.Input_LeftStrafe.Assign(keyQ);
  203.   MyThirdPersonNavigation.Input_RightStrafe.Assign(keyE);
  204.   MyThirdPersonNavigation.MouseLook := true; // TODO: assigning it from editor doesn't make mouse hidden in mouse look
  205.   MyThirdPersonNavigation.Init;
  206.   MyThirdPersonNavigation.AvatarHierarchy := AvatarTransform;
  207.   SandyLegs.AutoAnimation := 'LEGS_IDLE';
  208.   SandyTorso.AutoAnimation := 'TORSO_IDLE';
  209. end;
  210.  
  211. procedure TViewPlay.Stop;
  212. begin
  213.   FreeAndNil(Enemies);
  214.   FreeAndNil(DrawableImage);
  215.   inherited;
  216. end;
  217.  
  218. procedure TViewPlay.Update(const SecondsPassed: Single; var HandleInput: Boolean);
  219.  
  220.   // Test: use this to make AimAvatar only when *holding* right mouse button.
  221.   (*
  222.   procedure UpdateAimAvatar;
  223.   begin
  224.     if buttonRight in Container.MousePressed then
  225.       ThirdPersonNavigation.AimAvatar := aaHorizontal
  226.     else
  227.       ThirdPersonNavigation.AimAvatar := aaNone;
  228.  
  229.     { In this case CheckboxAimAvatar only serves to visualize whether
  230.       the right mouse button is pressed now. }
  231.     CheckboxAimAvatar.Checked := ThirdPersonNavigation.AimAvatar <> aaNone;
  232.   end;
  233.   *)
  234.  
  235. var
  236.   SandyAnims: array of String;
  237.   RayCastResult: TPhysicsRayCastResult;
  238.   MoveAmount: TVector3;
  239.   RayCastDirection: TVector3;
  240.   MaxDistance: Single;
  241.   SandyAirborne: Integer;
  242.   SandyIdling: Integer;
  243. begin
  244.   inherited;
  245.   { This virtual method is executed every frame (many times per second). }
  246.   SandyIdling := 0;
  247.   SandyAirborne := 0;
  248.   MaxDistance := 0.3;
  249.   MoveAmount := Vector3(0, -5, 0);
  250.   RayCastDirection := Vector3(0, -1, 0);
  251.   RayCastResult := AvatarRigidBody.PhysicsRayCast(
  252.   (AvatarTransform.Translation),
  253.   RayCastDirection,
  254.   0.3
  255.   );
  256.   if RayCastResult.Hit then
  257.   begin
  258.        SandyAirborne := 0;
  259.        LabelFps.Caption := 'FPS: PLACEHOLDER';
  260.       if SandyIdling = 0 then
  261.       begin
  262.            SandyIdling := 1;
  263.            SandyLegs.PlayAnimation('LEGS_IDLE', true);
  264.            SandyTorso.PlayAnimation('TORSO_IDLE', true);
  265.       end;
  266.   end
  267.   else
  268.       SandyIdling := 0;
  269.       begin
  270.            if SandyAirborne = 0 then
  271.            begin
  272.                 SandyAirborne := 1;
  273.                 SandyTorso.PlayAnimation('TORSO_AIRBORNE', true);
  274.                 SandyLegs.PlayAnimation('LEGS_AIRBORNE', true);
  275.             end;
  276.        AvatarTransform.Move(MoveAmount, false, true);
  277.        LabelFps.Caption := 'FPS: ' + Container.Fps.ToString
  278.        end;
  279.  
  280.       WalkNavigation.MouseLook := buttonRight in Container.MousePressed;
  281.   // UpdateAimAvatar;
  282.   end;
  283.  
  284. procedure TViewPlay.RenderOverChildren;
  285. begin
  286.   inherited;
  287.   { Do some direct drawing, to test this API works with ForceFixedFunction too. }
  288.   DrawRectangle(FloatRectangle(10, 10, 50, 50), Green);
  289.   DrawableImage.Draw(100, 10);
  290.  
  291.   { Render DrawableImage again, forcing alpha testing }
  292.   DrawableImage.Alpha := acTest;
  293.   DrawableImage.Draw(400, 10);
  294.   DrawableImage.Alpha := acAuto;
  295.  
  296.   FallbackFont.Print(700, 10, Red, 'Another sample text');
  297. end;
  298.  
  299. function TViewPlay.Press(const Event: TInputPressRelease): Boolean;
  300. var
  301.   HitByAvatar: TCastleTransform;
  302.   HitEnemy: TEnemy;
  303. begin
  304.   Result := inherited;
  305.   if Result then Exit; // allow the ancestor to handle keys
  306.  
  307.   { This virtual method is executed when user presses
  308.     a key, a mouse button, or touches a touch-screen.
  309.  
  310.     Note that each UI control has also events like OnPress and OnClick.
  311.     These events can be used to handle the "press", if it should do something
  312.     specific when used in that UI control.
  313.     The TViewPlay.Press method should be used to handle keys
  314.     not handled in children controls.
  315.   }
  316.  
  317.   if Event.IsMouseButton(buttonLeft) then
  318.   begin
  319.     SoundEngine.Play(SoundEngine.SoundFromName('shoot_sound'));
  320.   end;
  321.  
  322.   if Event.IsMouseButton(buttonRight) then
  323.   begin
  324.     MyThirdPersonNavigation.MouseLook := not MyThirdPersonNavigation.MouseLook;
  325.     Exit(true);
  326.   end;
  327.  
  328.   if Event.IsKey(keyF5) then
  329.   begin
  330.     Container.SaveScreenToDefaultFile;
  331.     Exit(true);
  332.   end;
  333.  
  334.   if Event.IsKey(keyEscape) then
  335.   begin
  336.     Container.View := ViewMenu;
  337.     Exit(true);
  338.   end;
  339.  
  340.   if (Event.IsKey(keyArrowUp) and SandyInRunning = false) then
  341.   begin
  342.     SandyInRunning := true;
  343.     SandyLegs.PlayAnimation('LEGS_RUN', true);
  344.     SandyTorso.PlayAnimation('TORSO_RUN', true);
  345.   end;
  346. end;
  347.  
  348. function TViewPlay.Release(const Event: TInputPressRelease): Boolean;
  349. begin
  350.   Result := inherited;
  351.   if Result then Exit; // allow the ancestor to handle keys
  352.  
  353.   { This virtual method is executed when user releases
  354.     a key, a mouse button, or touches a touch-screen.
  355.  
  356.     Note that each UI control has also events like OnPress and OnClick.
  357.     These events can be used to handle the "press", if it should do something
  358.     specific when used in that UI control.
  359.     The TViewPlay.Release method should be used to handle keys
  360.     not handled in children controls.
  361.   }
  362.  
  363.   if (Event.IsKey(keyArrowUp) and SandyInRunning = true) then
  364.   begin
  365.     SandyInRunning := false;
  366.     SandyLegs.PlayAnimation('BOTH_IDLE', true);
  367.     SandyTorso.PlayAnimation('BOTH_IDLE', true);
  368.   end;
  369. end;
  370.  
  371. procedure TViewPlay.ChangeCheckboxCameraFollows(Sender: TObject);
  372. begin
  373.   MyThirdPersonNavigation.CameraFollows := CheckboxCameraFollows.Checked;
  374. end;
  375.  
  376. procedure TViewPlay.ChangeCheckboxAimAvatar(Sender: TObject);
  377. begin
  378.   if CheckboxAimAvatar.Checked then
  379.     MyThirdPersonNavigation.AimAvatar := aaHorizontal
  380.   else
  381.     MyThirdPersonNavigation.AimAvatar := aaNone;
  382.  
  383.   { The 3rd option, aaFlying, doesn't make sense for this case,
  384.     when avatar walks on the ground and has Gravity = true. }
  385. end;
  386.  
  387. procedure TViewPlay.ChangeCheckboxDebugAvatarColliders(Sender: TObject);
  388. begin
  389.   DebugAvatar.Exists := CheckboxDebugAvatarColliders.Checked;
  390. end;
  391.  
  392. procedure TViewPlay.ChangeCheckboxImmediatelyFixBlockedCamera(Sender: TObject);
  393. begin
  394.   MyThirdPersonNavigation.ImmediatelyFixBlockedCamera := CheckboxImmediatelyFixBlockedCamera.Checked;
  395. end;
  396.  
  397. procedure TViewPlay.ChangeAirRotationControl(Sender: TObject);
  398. begin
  399.   MyThirdPersonNavigation.AirRotationControl := SliderAirRotationControl.Value;
  400. end;
  401.  
  402. procedure TViewPlay.ChangeAirMovementControl(Sender: TObject);
  403. begin
  404.   MyThirdPersonNavigation.AirMovementControl := SliderAirMovementControl.Value;
  405. end;
  406.  
  407. procedure TViewPlay.UpdateAfterChangeTransformation;
  408. begin
  409.   ButtonChangeTransformationAuto.Pressed := MyThirdPersonNavigation.ChangeTransformation = ctAuto;
  410.   ButtonChangeTransformationDirect.Pressed := MyThirdPersonNavigation.ChangeTransformation =  ctDirect;
  411.   ButtonChangeTransformationVelocity.Pressed := MyThirdPersonNavigation.ChangeTransformation =  ctVelocity;
  412.   {$ifdef CASTLE_UNFINISHED_CHANGE_TRANSFORMATION_BY_FORCE}
  413.   ButtonChangeTransformationForce.Pressed := ThirdPersonNavigation.ChangeTransformation = ctForce;
  414.   {$endif}
  415.  
  416.   { ctDirect requires to set up gravity without physics engine,
  417.     using deprecated TCastleTransform.Gravity.
  418.     See https://castle-engine.io/physics#_old_system_for_collisions_and_gravity }
  419.   AvatarRigidBody.Exists := MyThirdPersonNavigation.ChangeTransformation <> ctDirect;
  420.  
  421.   { Gravity means that object tries to maintain a constant height
  422.     (SceneAvatar.PreferredHeight) above the ground.
  423.     GrowSpeed means that object raises properly (makes walking up the stairs work).
  424.     FallSpeed means that object falls properly (makes walking down the stairs,
  425.     falling down pit etc. work). }
  426.   SandyTorso.Gravity := not AvatarRigidBody.Exists;
  427.   SandyLegs.Gravity := not AvatarRigidBody.Exists;
  428.   SandyHead.Gravity := not AvatarRigidBody.Exists;
  429. end;
  430.  
  431. procedure TViewPlay.ClickChangeTransformationAuto(Sender: TObject);
  432. begin
  433.   MyThirdPersonNavigation.ChangeTransformation := ctAuto;
  434.   UpdateAfterChangeTransformation;
  435. end;
  436.  
  437. procedure TViewPlay.ClickChangeTransformationDirect(Sender: TObject);
  438. begin
  439.   MyThirdPersonNavigation.ChangeTransformation := ctDirect;
  440.   UpdateAfterChangeTransformation;
  441. end;
  442.  
  443. procedure TViewPlay.ClickChangeTransformationVelocity(Sender: TObject);
  444. begin
  445.   MyThirdPersonNavigation.ChangeTransformation := ctVelocity;
  446.   UpdateAfterChangeTransformation;
  447. end;
  448.  
  449. procedure TViewPlay.ClickChangeTransformationForce(Sender: TObject);
  450. begin
  451.   {$ifdef CASTLE_UNFINISHED_CHANGE_TRANSFORMATION_BY_FORCE}
  452.   ThirdPersonNavigation.ChangeTransformation := ctForce;
  453.   {$endif}
  454.   UpdateAfterChangeTransformation;
  455. end;
  456.  
  457. end.

JPF12141999

  • Jr. Member
  • **
  • Posts: 88
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #13 on: February 22, 2024, 12:37:17 am »
Unfortuntely, even though I made sure to do things like put the code in the Start procedure in the correct order and be mindful of the slight difference I had to make to initiliaze the AvatarTransform, or making sure that I changed all of TViewPlay to TViewMain, and being extra sure I changed every single line and didn't accidentally leave a "TViewPlay" in there that will make the code end up not working,

I still get this error message, which suggests I have something wrong with the labels, but like almost all crash messages never specifies in more detail what I actually did wrong so I know what to correct in particular: Image attached.

Can someone please gently explain what I did wrong? Again, I didn't just copy paste the code mindlessly but made sure to follow the directions according to the exact style and order the original example suggested, and just added it to the code I had from earlier.

Is it perhaps that I am using "TViewPlay" rather than "TViewMain", even though I you will see in the original third person navigation, once you download the latest version of the Castle Game engine, example that it uses the word "TViewPlay" specifically in that particular file so I didn't want to change it for no good reason?

If that is the problem, I am thinking that perhaps I should add a new file called "TViewMain" seperately from TViewPlay, just like how I had to add "GameMyMesh" as a seperate code file from the other files, and not try to awkwardly kludge/bundle together the two files in one like I did here.

michalis

  • Full Member
  • ***
  • Posts: 143
    • Castle Game Engine
Re: Using the Castle Game Engine Without Any Prior Coding Experience
« Reply #14 on: February 22, 2024, 01:23:00 pm »
See at the error message.

It points to the line 128 of your unit GameViewMain.

Look what field do you access there. Are you sure a component with this name is present in the design?

You don't show the design (data/gameviewmain.castle-user-interface) so we cannot debug it. But I guess you didn't add a component with such name to your design.

And I'd advise to learn how to put your project in a GitHub -- https://github.com/ . This way you will be able to easily link to a full code + data of your project and others will be able to try it.

 

TinyPortal © 2005-2018