|
|
(2 intermediate revisions by 2 users not shown) |
Line 1: |
Line 1: |
| {{DISPLAYTITLE: Portal - Tutorial - Piston Panels}} | | {{DISPLAYTITLE: Portal - Tutorial - Piston Panels}} |
| {{back | Portal Level Creation}} | | {{back | Portal Level Creation}} |
| | |
| ==Introduction== | | ==Introduction== |
| This guide will show you how to create Piston Panels. These are one of the most seen objects in Portal. | | This guide will show you how to create Piston Panels. These are one of the most seen objects in Portal. |
Line 39: |
Line 40: |
| Position these brushes right below the beginning of the brushes that create the hole entrance. Remember to tie these to a <code>func_detail</code>. | | Position these brushes right below the beginning of the brushes that create the hole entrance. Remember to tie these to a <code>func_detail</code>. |
| [[File:Portal_Piston_Panel_Grates.png|thumb|left|300px|Grates]] | | [[File:Portal_Piston_Panel_Grates.png|thumb|left|300px|Grates]] |
| {{clr}}
| |
|
| |
| ==The Piston Brushes==
| |
| ===Base brush===
| |
| First, create a 12-sided cylinder with a size of <code>32w*32l*192h</code>. Texture the sides with <code>metal/metal_lift001</code> and <code>metal/metal_lift001_gradient</code>, and the top and bottom with <code>nodraw</code>.
| |
| [[File:Piston base.png|thumb|left|Textured piston base with pit hidden]]
| |
| {{clr}}
| |
|
| |
| ===Inner cylinders===
| |
| When creating the inner cylinders, be sure to duplicate the existing cylinders, due to the fact that creating new smaller cylinders will align them to the grid differently, which will cause the joint pieces to have illegal geometry.
| |
|
| |
| Every successive inner piston cylinder should be 8 units smaller in diameter than the previous one. Texture all successive cylinders with <code>metal/metal_lift001</code>. Note that the length of the piston cylinders should be less than the one before it. The size of these piston brushes need to add up to at least the target extension height of the piston. In this guide, the panel will have a target extension of 256 units. Once at the full height, resize the top cylinder to be 16 units below a major gridline (32, 64, 128 units), so you have room for the panel on top.
| |
|
| |
| If you require the pistons to extend farther, you can make the largest piston cylinder wider and/or longer to accommodate. Note that you may need to increase the depth of the pit as well.
| |
| [[File:Piston additional cylinders.png|thumb|left|Extended piston with 2 additional cylinders, each with a height of 192 units.]]
| |
| {{clr}}
| |
|
| |
| ===Joint pieces===
| |
| To create the joints between pistons, create a <code>32w*32l*8h</code> 12-sided cylinder brush. Texture the sides with <code>metal/metalwall_bts_005a</code>. Position it directly on top of the base brush.
| |
|
| |
| Next, use the vertex tool to move the top vertices to the next smallest piston size. Select the vertices in the 3D view before moving them.
| |
| [[File:Piston joint.png|thumb|left|Piston joint textured and positioned above base brush]]
| |
| {{clr}}
| |
|
| |
| Repeat this process for all the smaller pistons, except the last one. For the last one, simply duplicate the last one and flip it over. Position it at the top of the cylinder.
| |
| [[File:Piston joints with top.png|thumb|left|Joints on all piston pieces]]
| |
| {{clr}}
| |
|
| |
| Finally, for each piston piece, select the brush and the joint above it, and tie them to a <code>func_brush</code>. For the bottom one, as it is not going to move, you can simply tie it to a <code>func_detail</code>.
| |
| [[File:Piston details.png|thumb|left|Piston brushes tied to entities]]
| |
| {{clr}}
| |
|
| |
| === Creating the panel ===
| |
| For the panel, create a <code>128w*128l*8h</code> brush textured on the top with your floor or wall texture, on the sides with <code>signage/hazard_orange_03b</code>, and on the bottom with <code>metal/citadel_metalwall060a</code>. Position it on top of your top piston cylinder.
| |
| [[File:Piston panel on top.png|thumb|left|Panel textured and positioned atop the piston cylinders]]
| |
| {{clr}}
| |
|
| |
| === Panel Details ===
| |
| The panel details can be created by creating a <code>128w*128l*4h</code> brush textured with <code>plastic/plasticwall001b</code>. Use the clipping tool to remove the corners on the bottom (as seen in the image below)
| |
| [[File:Panel detail.png|thumb|left|Panel detail brush with bottom corners removed]]
| |
| {{clr}}
| |
|
| |
| Next, use the clipping tool to carve out a <code>112w*112l</code> hole in the detail brush. The sides of the detail brush should be 8 units wide, as seen in the image below. Then, tie it, along with the panel surface itself, to a <code>func_brush</code>.
| |
| [[File:Panel details.png|thumb|left|Top panel details complete and textured]]
| |
| {{clr}}
| |
|
| |
| ==Making it Move==
| |
| If all you need is a static panel that doesn't move, you can simply stop here. If it doesn't move, tie all the panel pieces to <code>func_detail</code> entities instead of <code>func_brush</code>.
| |
|
| |
| Making the panel move in and out uses a series of <code>func_door</code> entities. If you only have one moving piston cylinder, however, you can accomplish this using a <code>func_movelinear</code> entity instead. However, due to a bug with the <code>func_movelinear</code> entity, parenting doesn't work. This is why this tutorial is going to focus on the <code>func_door</code> implementation.
| |
|
| |
| For the sake of this tutorial, the panel will move from fully recessed to 256 units extended.
| |
|
| |
| === Creating the <code>func_door</code> entities ===
| |
| For the process of creating the func_doors, it can get cramped with all the entities overlapping, so it is recommended to hide all the piston cylinders for now.
| |
|
| |
| Since the target extension distance is 256 units, that means that each piston cylinder must move 128 units, since we have 2 inner cylinders.
| |
|
| |
| For the first extension, create a <code>16w*16l*128h</code> brush textured with <code>nodraw</code>. Tie this to a <code>func_door</code>. Set the following property values:
| |
|
| |
| {| class="standard-table sortable"
| |
| |-
| |
| ! Property Name !! Value
| |
| |-
| |
| | Name || piston_cylinder1
| |
| |-
| |
| | Speed || 15
| |
| |-
| |
| | Delay Before Reset || -1
| |
| |-
| |
| | Move Direction || -90 0 0
| |
| |}
| |
| {{clr}}
| |
|
| |
| Set the speed to the desired movement speed divided by the number of piston segments. For a speed of 30, the speed values in this case need to be 15.
| |
|
| |
| Be sure to also uncheck the <code>Touch Opens</code> flag.
| |
|
| |
| [[File:Piston cylinder1.png|thumb|left|Cylinder 1's func_door entity, centered in the hole]]
| |
| {{clr}}
| |
|
| |
| For the second extension, duplicate this entity, and change the following property values:
| |
|
| |
| {| class="standard-table sortable"
| |
| |-
| |
| ! Property Name !! Value
| |
| |-
| |
| | Name || piston_cylinder2
| |
| |-
| |
| | Parent || piston_cylinder1
| |
| |}
| |
| {{clr}}
| |
|
| |
| Repeat this for however many extensions you have. Note that the position and height of these <code>func_door</code> entities is not super important, however, if you want to make them different heights so they don't overlap, then simply change the <code>Lip</code> property value to be the entity height minus the desired extension distance. In this case, the height of the <code>func_door</code> entities are 32 units each, so the Lip value is 32 - 128 or -96.
| |
| [[File:Func door entities.png|thumb|left|The func_door entities]]
| |
| {{clr}}
| |
|
| |
| === Adding Moving Logic ===
| |
| This is where this tutorial can get a little bit difficult. This setup moves all the cylinders simultaneously, and is easier than the alternative of extending one after the other.
| |
|
| |
| Firstly, create 2 <code>ambient_generic</code> entities. Set the property values to the following:
| |
|
| |
| {| class="standard-table sortable"
| |
| |-
| |
| ! Property Name !! Value
| |
| |-
| |
| | Name || piston_sound_start
| |
| |-
| |
| | Sound Name || apc_engine_start
| |
| |-
| |
| | SourceEntityName || piston_cylinder1
| |
| |}
| |
| {{clr}}
| |
|
| |
| {| class="standard-table sortable"
| |
| |-
| |
| ! Property Name !! Value
| |
| |-
| |
| | Name || piston_sound_stop
| |
| |-
| |
| | Sound Name || apc_engine_stop
| |
| |-
| |
| | SourceEntityName || piston_cylinder1
| |
| |}
| |
| {{clr}}
| |
|
| |
| On the <code>piston_sound_start</code> entity, make sure to uncheck the <code>Is NOT looped</code> flag.
| |
|
| |
| Next, create a <code>math_counter</code> entity, and set the following property values:
| |
|
| |
| {| class="standard-table sortable"
| |
| |-
| |
| ! Property Name !! Value
| |
| |-
| |
| | Name || piston_counter
| |
| |-
| |
| | Maximum Legal Value || 2
| |
| |}
| |
| {{clr}}
| |
|
| |
| On the <code>math_counter</code> entity, set the following outputs:
| |
|
| |
| {| {{OutputsTable}}
| |
| | [[File:Io21.png]] || OnHitMax || piston_sound_start || StopSound || <none> || 0.00 || No
| |
| |-
| |
| | [[File:Io21.png]] || OnHitMax || piston_sound_stop || PlaySound || <none> || 0.00 || No
| |
| |-
| |
| | [[File:Io21.png]] || OnHitMin || piston_sound_start || StopSound || <none> || 0.00 || No
| |
| |-
| |
| | [[File:Io21.png]] || OnHitMin || piston_sound_stop || PlaySound || <none> || 0.00 || No
| |
| |}
| |
| {{clr}}
| |
|
| |
| Now, create a <code>logic_relay</code> entity. Name it <code>piston_extend</code> and give it the following outputs:
| |
|
| |
| {| {{OutputsTable}}
| |
| | [[File:Io21.png]] || OnTrigger || piston_cylinder1 || Open || <none> || 0.00 || No
| |
| |-
| |
| | [[File:Io21.png]] || OnTrigger || piston_cylinder2 || Open || <none> || 0.00 || No
| |
| |-
| |
| | [[File:Io21.png]] || OnTrigger || piston_sound_start || PlaySound || <none> || 0.00 || No
| |
| |}
| |
| {{clr}}
| |
|
| |
| Similarly, create another <code>logic_relay</code> entity. Name it <code>piston_retract</code> and give it the following outputs:
| |
|
| |
| {| {{OutputsTable}}
| |
| | [[File:Io21.png]] || OnTrigger || piston_cylinder1 || Close || <none> || 0.00 || No
| |
| |-
| |
| | [[File:Io21.png]] || OnTrigger || piston_cylinder2 || Close || <none> || 0.00 || No
| |
| |-
| |
| | [[File:Io21.png]] || OnTrigger || piston_sound_start || PlaySound || <none> || 0.00 || No
| |
| |}
| |
| {{clr}}
| |
|
| |
| Lastly, on each of the <code>func_door</code> entities, set the following outputs:
| |
|
| |
| {| {{OutputsTable}}
| |
| | [[File:Io21.png]] || OnFullyOpen || piston_counter || Add || 1 || 0.00 || No
| |
| |-
| |
| | [[File:Io21.png]] || OnFullyClosed || piston_counter || Subtract || 1 || 0.00 || No
| |
| |}
| |
| {{clr}}
| |
|
| |
| === Parenting brushes ===
| |
| Now we need to parent the piston brushes we created earlier to the<code>func_door</code> entities.
| |
|
| |
| First, move the piston brushes into place in a retracted state.
| |
| [[File:Piston retracted.png|thumb|left|The piston, retracted]]
| |
| {{clr}}
| |
|
| |
| Next, select each piston cylinder and parent it to its respective <code>func_door</code> entity. Parent the piston panel to the last cylinder door.
| |
|
| |
| == Usage ==
| |
| Simply send a <code>Trigger</code> input to the <code>piston_extend</code> and <code>piston_retract</code> to extend and retract the piston, respectively.
| |
|
| |
| [[File:Panel retracted ingame.png|thumb|left|Panel retracted ingame]]
| |
| [[File:Panel extended ingame.png|thumb|left|Panel extended ingame]]
| |
| {{clr}} | | {{clr}} |
|
| |
|